The purpose of this notebook is to demonstrate some of what is possible for the mining and visualisation of a text. We’ll go through some of the basic tools and do some data visualisation to demonstrate what is possible. If this kind of analysis is not directly relevant to your research, hopefully you can use this time to practice different data visualisation techniques and become more familiar with seeking out answers to questions about programming R code.

1 Setting up

In this session, we’ll be using a few packages, but they’ll be introduced as we go. Firstly, we’ll need to load in tidyverse and tm (text mining). We will not explicitly be using SnowballC at this time, but it may be useful to know about as it helps with lemmatization for word frequency analysis in several languages. It is one of tm’s dependencies, and may be doing a bit of background work.

library(tidyverse)
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.5
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
# R must be at least 3.3.1 for `tm` and `slam` to work.
# library("SnowballC")
library(tm)
Loading required package: NLP

Attaching package: ‘NLP’

The following object is masked from ‘package:ggplot2’:

    annotate

Before we get started working with the actual data, we’re going to create a custom function for plotting multiple ggplot graphs in a single image. This function was copied from Cookbook for R.

# Multiple plot function
#
# ggplot objects can be passed in ..., or to plotlist (as a list of ggplot objects)
# - cols:   Number of columns in layout
# - layout: A matrix specifying the layout. If present, 'cols' is ignored.
#
# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE),
# then plot 1 will go in the upper left, 2 will go in the upper right, and
# 3 will go all the way across the bottom.
#
multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) {
  library(grid)
  # Make a list from the ... arguments and plotlist
  plots <- c(list(...), plotlist)
  numPlots = length(plots)
  # If layout is NULL, then use 'cols' to determine layout
  if (is.null(layout)) {
    # Make the panel
    # ncol: Number of columns of plots
    # nrow: Number of rows needed, calculated from # of cols
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                    ncol = cols, nrow = ceiling(numPlots/cols))
  }
 if (numPlots==1) {
    print(plots[[1]])
  } else {
    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout))))
    # Make each plot, in the correct location
    for (i in 1:numPlots) {
      # Get the i,j matrix positions of the regions that contain this subplot
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))
      print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}

2 Reading in corpus

For this demonstration, I’ve selected a corpus of Shakespeare’s plays and adapted some code from a Kaggle notebook. Once you understand what the code here is doing, it can easily be adapted to a corpus of any structure or content.

We should take a look at how the corpus is structured so we know what we’re dealing with.

shak <- read.csv("../data/Shakespeare_data.csv",header = TRUE, as.is = TRUE)

These are the first ten lines of the corpus.

Each column is labeled and the content of the column is consistent for each row (all 111396 of them!). Some of the rows may not be useful. Some contain empty cells (labeled NA). Some contain a lot of information and we might need to do some processing on them before we can use the information quantitatively (for example the column PlayerLine).

3 Word frequency

The first thing we’ll look at is word frequency, or how often a string (in this case “love”) occurs in the data frame. To do this, we must identify every time the word “love” appears and highlight it in a way so that it can be counted based on different properties of its environment (e.g., by play, by player, by scene, etc).

Here are the first 10 rows of a data frame that contains the number of times “love” appears in each play. It’s been sorted in descending order, but doesn’t contain any other information about where and when the word occurs.

# play level word frequency
plays <- unique(shak$Play)
loveFreq<-numeric()
for (i in 1:length(plays)){
    text <- Corpus(VectorSource(paste(shak[shak$Play==plays[i],]$PlayerLine,collapse=" ")))
    text <- tm_map(text, removePunctuation)
    text <- tm_map(text, PlainTextDocument)
    text <- tm_map(text, removeWords, stopwords('english'))
    
    # stemming to merge all "loved", "loving" into one   
    text <- tm_map(text, stemDocument)
    tdm  <- TermDocumentMatrix(text)
    
    loveFreq[i]<-as.numeric(slam::row_sums(tdm)["love"]) # `slam` must be installed for this to work
  }
lPlay <- data.frame(plays,loveFreq)
lPlay <- na.omit(lPlay)
# order the plays based on the occurence of love
lPlay<-lPlay[order(-lPlay$loveFreq),]
lPlay

We can also look at which players say “love” the most over the course of their appearences. These are only the top 10 players who use the word “love” most.

# player level word frequency
players <- unique(shak$Player)
loveFreq <- numeric()
for (i in 1:length(players)){
    text <- Corpus(VectorSource(paste(shak[shak$Player==players[i],]$PlayerLine,collapse=" ")))
    text <- tm_map(text, removePunctuation)
    text <- tm_map(text, PlainTextDocument)
    text <- tm_map(text, removeWords, stopwords('english'))
    text <- tm_map(text,stemDocument)
    
    tdm  <- TermDocumentMatrix(text)
    
    loveFreq[i] <- as.numeric(slam::row_sums(tdm)["love"])
  }
lPlayer <- data.frame(players,loveFreq)
lPlayer <- na.omit(lPlayer)
#order
lPlayer <- lPlayer[order(-lPlayer$loveFreq),]
lPlayer

3.1 Exploring someone else’s code

Let’s go through the for-loop in more detail to understand what it’s doing.

How many players are in this vector? (Answer: 935)
That’s the number of times this for-loop will cycle, each time incrementing the loop number by 1.

for (i in 1:length(players)){

The first line of the loop uses Corpus() and VectorSource(). What do these functions do?
We can check the Help files by typing ?VectorSource(). It looks like both deal with the structure and metadata of a corpus. Importantly, we can see [i] in the code though. This is the same i from the previous line, which cycles through each of the players in the vector.
What does that tell us about the first pass through this loop?

text <- Corpus(VectorSource(paste(shak[shak$Player==players[i],]$PlayerLine,collapse=" ")))

The next few lines all use the function tm_map(), which performs a transformation on the corpus. In this case, the corpus is the subset we’ve called text in the above line. Judging from the syntax, it appears that they respectively remove punctuation from the text, convert it to a plain text document, remove English stop words, and merge all words with the same stems or lemmas (i.e., “love”, “loving”, “loved”, etc).

text <- tm_map(text, removePunctuation)
text <- tm_map(text, PlainTextDocument)
text <- tm_map(text, removeWords, stopwords('english'))
text <- tm_map(text, stemDocument)

This line turns the resulting vector into a term-document matrix, which is basically a way of identifying where each word occurs numerically, which will allow us to count the words in the next line.

tdm  <- TermDocumentMatrix(text)

A simple TDM may look like this:

enter exit hamlet ophelia and
text_1 1 0 1 0 0
text_2 1 0 0 1 0
text_3 0 1 1 1 1

What might these three “texts” say?

The last line has several things going on. We can figure out what each function does by trying it on its own.

loveFreq[i] <- as.numeric(slam::row_sums(tdm)["love"])

First, we can see what tdm looks like when i = 1:

tdm
<<TermDocumentMatrix (terms: 27, documents: 2)>>
Non-/sparse entries: 27/27
Sparsity           : 50%
Maximal term length: 12
Weighting          : term frequency (tf)

Hmmm, that doesn’t look interpretable by a human. Maybe row_sums() from the package slam will help?

slam::row_sums(tdm)
         act       adjoin    antechamb        apart     bardolph        blunt 
           1            1            1            1            1            1 
   boarshead       cloten         earl    eastcheap        enter     falstaff 
           1            1            1            1            3            1 
       henri       imogen         john         king    lancaster       london 
           1            1            1            1            1            1 
        lord        other        palac        scene          sir       tavern 
           2            1            1            1            1            1 
         the       walter westmoreland 
           2            1            1 

Ah ha! That looks more intelligible. Which player is this for?
(Answer = ; if you want to see what different players are doing, you can change the value of i by hand and re-run the code.)

So what is ["love"] doing in the code?

slam::row_sums(tdm)["love"]
<NA> 
  NA 

Ah, “love” doesn’t exist in this row (where i = 1). Let’s see what it would look like if we were actually interested in “enter”:

slam::row_sums(tdm)["enter"]
enter 
    3 

There are two elements that are being printed out here: the word (“enter”) and the sum (3). That’s what as.numeric() will help us with:

as.numeric(slam::row_sums(tdm)["enter"])
[1] 3

This means that the sum of occurrences of “love” is now entered into index [i] in the vector loveFreq, and the loop begins again until i = length(players).

Finally, we close off the for-loop.

}

Can you figure out how to use the code above to create a data frame with the following two conditions?:

  • A list of players
  • Ordered by frequency of the word death

(possible solution below)

























# create the vectors you need
# players <- unique(shak$Player) # <-- we already made this one before, but we can do it again too
deathFreq <- numeric() # <-- create a vector of class "numeric"

# create the for-loop to calculate frequency
for (i in 1:length(players)){
    text <- Corpus(VectorSource(paste(shak[shak$Player==players[i],]$PlayerLine,collapse=" ")))
    text <- tm_map(text, removePunctuation)
    text <- tm_map(text, PlainTextDocument)
    text <- tm_map(text, removeWords, stopwords('english'))
    text <- tm_map(text,stemDocument)
    
    tdm  <- TermDocumentMatrix(text)
    
    deathFreq[i] <- as.numeric(slam::row_sums(tdm)["death"])
  }

# create the data frame
dPlayer <- data.frame(players,deathFreq)
dPlayer <- na.omit(dPlayer)
# order the data frame from most to least frequent
dPlayer <- dPlayer[order(-dPlayer$deathFreq),]
dPlayer

4 Visualising a corpus

Maybe you are curious how the longer and shorter plays compare in word frequency. Instead of hand-counting each, we can graph and order them. Based on this graph, you don’t need to know exactly how long each is, but you can see that Othello is much longer than Loves Labours Lost, which can inform how you approach the comparison later on.

shak %>%
  # `Play` is the factor we are interested in
  group_by(Play) %>% 
  # summarise the content of `Play` by counting how many instances there are of each (`n`)
  summarise(n = n()) %>% 
  # reorder() allows us to sort them greatest to least
  ggplot(., aes(x=reorder(Play, n),y=n)) + 
    # stat="identity" is crucial for ggplot to know each `n` is the length of the bar
    geom_bar(stat="identity") + 
    # this way, the play names will be horizontal and fully displayed
    coord_flip() + 
    ggtitle("Length of Shakespeare's plays") +
    xlab("Play") +
    ylab("Number of lines")

4.1 Perception of frequency

Within a single play, maybe we want to know which characters are the chattiest. We can visualise the number of lines of text per character to get a sense of who is dominating the stage.

shak %>%
  filter(Play == "Hamlet") %>% # limit our dataset to only the play "Hamlet"
  group_by(Player) %>%
  summarise(n = n()) %>%
  ggplot(., aes(x=reorder(Player, n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("Speech in Hamlet") +
    xlab("Player") +
    ylab("Number of lines") #+

    #scale_y_log10()

We can also look across plays for frequency. By comparing which plays have the word “love” the most often, we might be able to group them (perceptually) into plays about love and those that are not. Maybe?

lPlay %>%
  ggplot(., aes(x=reorder(plays, loveFreq),y=loveFreq)) +
    geom_bar(aes(),stat="identity") +
    coord_flip() +
    ggtitle("Love in each play") +
#    theme(legend.position="none") +
    xlab("Play") +
    ylab("frequency of the word 'love'") +
    theme(legend.position = "none")

4.2 Comparing across subsets

One thing that graphs can do very easily is give you a way to identify trends when you sort events (e.g., plays) into multiple different categories. For instance, the frequency graph above is interesting, but there are so many plays and as a non-expert, I can’t tell you what each is about, what style it is written in, or whether I’d expect it to be about “love” or not. So, we can add another dimension of information.

In the following graph, each color represents a different category (as determined by Wikipedia’s First Folio page, plus information about the “late romances”). Now, we can see if there are trends for different categories to mention “love” more or less than the others.

It seems to me that comedies and tragedies discuss “love” the most, whereas histories and the late romances discuss it the least. Is this intuitive? Maybe. But there’s a problem. A Comedy of Errors has the fewest mentions of “love”, but it’s also the shortest play, so it has the fewest words overall. What we really want to see is the proportion of “love”-frequency per play, not the raw counts. To do that, we have to add in the total length of each play to the data frame, but we’ve lost that information in lPlayCat and lPlay. How might you add that information in to the lPlayCat dataframe?

(possible solution below)

























# create new data frame for lengths of plays
playLength <- shak %>%
  group_by(Play) %>%
  summarise(n = n()) %>%
  transmute(plays=Play,length=n) # rename columns that we want, which happens to be both of them
# add column to lPlayCat
lPlayCatLen <- lPlayCat %>%
  left_join(.,playLength)
Joining, by = "plays"
Column `plays` joining factor and character vector, coercing into character vector

Now we can take a look to see how raw and proportional graphs compare.

lPlayCatLen %>%
  mutate(proportion = loveFreq/length) %>%
  ggplot(., aes(x=reorder(plays, proportion),y=proportion)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Love in each play") +
#    theme(legend.position="none") +
    xlab("Play") +
    ylab("proportional frequency of the word 'love'")

Not a whole lot has changed, but I think the distribution of comedies and tragedies is even more pronounced. And, we have more information about A Comedy of Errors, which is still very close to the bottom of the graph. Not every comedy is about love, it seems.

Finally, we can generate these same types of graphs for different subgroups, too. We’ll have to add in $category to shak as well, but we already have the vectors for each level of category, so it’s pretty easy to do.

# create boolean vectors for four play categories in `shak`
comedy  <- shak$Play %in% comedies
romance <- shak$Play %in% romances
history <- shak$Play %in% histories
tragedy <- shak$Play %in% tragedies
# create new column with these four categories
shakCat <- shak %>%
  mutate(category = case_when(comedy == TRUE ~ "comedy",
                              romance == TRUE ~ "romance",
                              history == TRUE ~ "history",
                              tragedy == TRUE ~ "tragedy"))

Here’s one example, where we look at the number of lines each player has, focusing only on players who have greater than 700 lines. We can also see if there are any trends in these top speakers by play category. It seems to me that the histories dominate, but Hamlet and Iago dominate the scene (so to speak).

shakCat %>%
  group_by(Play,Player,category) %>%
  summarise(n = n()) %>%
  filter(n > 700) %>%
  ggplot(., aes(x=reorder(Player, n),y=n)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Number of lines by character") +
#    theme(legend.position="none") +
    xlab("Player") +
    ylab("Number of lines")

Is this because histories and tragedies tend to be longer plays, overall? Quite possibly:

shakCat %>%
  group_by(Play,category) %>%
  summarise(n = n()) %>%
  ggplot(., aes(x=reorder(Play, n),y=n)) +
    geom_bar(aes(fill=category),stat="identity") +
    coord_flip() +
    ggtitle("Length of Shakespeare's plays") +
    theme(legend.position="none") +
    xlab("Play") +
    ylab("Number of lines")

5 N-grams

Single words might not be able to tell us much about the texts, which is why examining collocations is such a popular technique. What’s the difference between “the king”, “kill the king”, and “kiss the king”? A lot, but we won’t know if we only look for instances of “king”. There is where n-grams become useful. N-grams are sets of adjacent words, calculated by assigning a number to ‘n’. That is, if we want sets of two words, we talk about bigrams. If we want sets of three words, we talk about trigrams.

5.1 Pre-processing the corpus

First, we can look at the corpus as a list of words, rather than a list of lines (by act and scene). What is unnest_tokens() doing here?

Now, we can automate the process of counting how often each word occurs. But, of course, certain words are going to be extremely common, and those words are unlikely to be informative.

Once we’ve filtered out our stop-words, the most frequent words look quite different.

However, Shakespeare uses a lot of words that aren’t in our default stop-word list, so we can append our own custom list.

word <- c(NA,"thou","thee","thy","thine","dost","shalt","wilt","hast","hath","scene","tis","ii","iii","iv","v","vi","vii")
lexicon <- rep("shakespeare",length(word))
new_stop <- cbind(word,lexicon)
shak_stop <- rbind(new_stop,stop_words)

Here is a visualisation of how stop words affect the corpus.

p1 <- shak %>%
  as_tibble(.) %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  count(word, sort = TRUE) %>%
  filter(n>800) %>%
  ggplot(., aes(x=reorder(word,n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("No stop words")
p2 <- shak %>%
  as_tibble(.) %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  anti_join(stop_words) %>%
  count(word, sort = TRUE) %>%
  filter(n>800) %>%
  ggplot(., aes(x=reorder(word,n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("Default stop words")
p3 <- shak %>%
  as_tibble(.) %>%
  unnest_tokens(tbl=., input = PlayerLine, output = word) %>%
  anti_join(shak_stop) %>%
  count(word, sort = TRUE) %>%
  filter(n>800) %>%
  ggplot(., aes(x=reorder(word,n),y=n)) +
    geom_bar(stat="identity") +
    coord_flip() +
    ggtitle("Custom stop words")
Column `word` joining character vector and factor, coercing into character vector
multiplot(p1,p2,p3,cols=3)

5.2 Comparing across plays

Before we try to compare across plays, let’s see what the most common bigrams are overall (after being filtered by the custom stop words list).

shak %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE)

5.2.1 1-grams (just regular token frequency)

If we choose a subset of words, we can look at how they are distributed across different plays. In this case, we can compare death, king, love, and sweet across six plays. Unsurprisingly, Romeo and Juliet uses the word love more than any other play, although Midsummer Night’s Dream is close. King is also much more common in plays about kings (surprise, surprise).

But there are more interesting words you could compare, certainly. These are just one example.

5.2.2 Bigrams

It’s probably much more interesting to look at collocation than simple word frequency. After all, it gives more context. However, it also reduces the number of tokens substantially.

Here, we can visualise three pairs of gendered noun phrases:

Masculine Feminine
my lord my lady
my father my mother
my husband my wife

What kinds of information can we see in the graph?

5.3 Networks

#install.packages("igraph")
#install.packages("ggraph")
library(igraph)

Attaching package: ‘igraph’

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(ggraph)
library(grid)
a <- grid::arrow(type = "closed", angle=22.5, length = unit(.1, "inches"))

I think the most exciting way to use n-grams is probably network graphs. These graphs show what words co-occur, and in what order. Moreover, they can encode a number of dimensions visually, which would be very difficult to calculate by hand or plot in a more standard quantitative method.

5.3.1 Bigrams

From the list of bigrams we can generate from our corpus, we can plot a network graph in which the shade of the connection between nodes indicates the frequency of the bigram (darker means more frequent). Moreover, these connections (“edges”) are directional, so we can see which order the words are occuring in.

We can also use these plots to compare across plays (although token frequency begins to drop precipitously). Here, we can see that Twelfth Night has notable connections between items of clothing (yellow stockings, cross gartered), whereas Hamlet and Romeo and Juliet have more tokens referring to people and their stage directions. Hamlet additionally has a notable number of “father’s death” bigrams, while Romeo and Juliet mentions “county Paris” frequently.

p1 <- shak %>%
  filter(Play=="Hamlet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()
p2 <- shak %>%
  filter(Play == "Twelfth Night") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkred", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "salmon", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()
p3 <- shak %>%
  filter(Play == "Romeo and Juliet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkgreen", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "green2", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()
multiplot(p1,p2,p3,cols=3)

If we exclude stage directions and compare across six plays, we start to see distinct themes appear.

p1 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play=="Hamlet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Hamlet")
p2 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Twelfth Night") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkred", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "salmon", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Twelfth Night")
p3 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Romeo and Juliet") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkgreen", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "green2", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Romeo and Juliet")
p4 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Othello") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkorange", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "orange", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Othello")
p5 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "Henry IV") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="cadetblue4", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "cyan", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("Henry IV")
p6 <- shak %>%
  filter(ActSceneLine != "") %>%
  filter(Play == "The Tempest") %>%
  unnest_tokens(input = PlayerLine, output = bigram, token = "ngrams", n = 2) %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  count(word1, word2, sort = TRUE) %>%
  filter(n > 3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="violet", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "magenta", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void() +
    ggtitle("The Tempest")
multiplot(p1,p2,p3,p4,p5,p6,cols=3)

All of these words are found directly adjacent. What about when words are slighting further apart? That is, language is not linear. How can we visualise relationships between topics that are somewhat less immediate?

5.3.2 Trigrams

One of the simplest ways to visualise more distant connections is the include more words in each network. Here is a graph of trigrams for the entire corpus, excluding stage directions. (Actually, I think this graph may be of only the first two elements of each trigram, but I will have to sort that out later.)

shak %>%
  filter(ActSceneLine != "") %>%
  unnest_tokens(input = PlayerLine, output = trigram, token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  filter(!word3 %in% shak_stop$word) %>% # filters stop words from third column
  count(word1, word2, word3, sort = TRUE) %>%
  filter(n > 2) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), repel=TRUE) + 
    theme_void()

What happens if we treat the first pair and second pair of trigrams as separate bigrams and graph them as before?

We should be able to visualise longer distance relations of these collocations. This is only a taste of what kinds of network graphs can be generated, since the type of graph and content will vary tremendously based on your unique research questions.

w1w2 <- shak %>%
  filter(ActSceneLine != "") %>%
  unnest_tokens(input = PlayerLine, output = trigram, token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  filter(!word3 %in% shak_stop$word) %>% # filters stop words from third column
  count(word1, word2, word3, sort = TRUE) %>%
  mutate(set = 1) %>%
  transmute(word1=word1,word2=word2,n=n,set=set)
w2w3 <- shak %>%
  filter(ActSceneLine != "") %>%
  unnest_tokens(input = PlayerLine, output = trigram, token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ") %>% # separates bigram into two columns, one for each word
  filter(!word1 %in% shak_stop$word) %>% # filters stop words from first column
  filter(!word2 %in% shak_stop$word) %>% # filters stop words from second column
  filter(!word3 %in% shak_stop$word) %>% # filters stop words from third column
  count(word1, word2, word3, sort = TRUE) %>%
  mutate(set = 2) %>%
  transmute(word1=word2,word2=word3,n=n,set=set)
wXwY <- bind_rows(w1w2,w2w3)
wXwY %>%
  filter(n>=3) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
    geom_node_point(color = "lightblue", size = 5) +
    geom_edge_link(aes(edge_alpha = n), edge_colour="darkblue", show.legend = TRUE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
    geom_node_text(aes(label = name), alpha=.75, repel=TRUE) + # , vjust = 1, hjust = 1) +
    theme_void()

6 Heatmaps

Another visualisation tool that can be very helpful for showing distributions of events across a structure is the heatmap. A heatmap is a complex histogram, which allows multiple subsets of the data to be compared side-to-side.

In this example, we can look at the variability of length of subsections (acts and scenes) as determined by number of words. First, let’s take a look at the numbers. Since there are so many acts and scenes across all the plays, it is unweildy to look at such a table.

shak %>%
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Play,act,scene, sort=TRUE) %>%
  transmute(play=Play, act=as.numeric(act), scene=as.numeric(scene), n=n) %>%
  group_by(play)

We could look at a series of histograms to see how plays vary in their distribution…

shak %>%
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Play,act, sort=TRUE) %>%
  transmute(play=Play, act=as.integer(act), n=n) %>%
  ggplot(aes(x=act)) + 
    geom_histogram(aes(y = n), stat="identity") + facet_wrap(~play)
Ignoring unknown parameters: binwidth, bins, pad

However, it is much easier and quicker to extract this information from a heatmap (although the precise numbers are lost in this version).

shak %>%
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Play,act, sort=TRUE) %>%
  transmute(play=Play, act=as.integer(act), n=n) %>%
  ggplot(aes(x=act,y=reorder(play, n))) + 
    geom_tile(aes(fill = n), colour = "white") + scale_fill_gradient(low = "white", high = "steelblue")

Let’s focus in on a subset of plays in order to simplify the visualisation. From a simple visual inspection, it appears that Act V tends to be the lightest on words, with the very notable exception of Love’s Labour’s Lost. Otherwise, Act I is generally fairly heavy on words, and Act II tends to be a bit lighter. Armed with that (superficial) observation, we might be able to check whether our eyes deceive us or whether there’s something to it. (But I’ll leave that exploration for another time.)

shak %>%
  filter(Play == "Hamlet" | Play == "King John" | Play == "The Tempest" | 
           Play == "Cymbeline" | Play == "Measure for measure" | Play == "Timon of Athens" | 
           Play == "Richard III" | Play == "Loves Labours Lost" | Play == "A Winters Tale" | 
           Play == "Othello" | Play == "Romeo and Juliet" | Play == "Henry V") %>% 
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Play,act, sort=TRUE) %>%
  transmute(play=Play, act=as.integer(act), n=n) %>%
  ggplot(aes(x=act,y=reorder(play, n))) + 
    geom_tile(aes(fill = n), colour = "white") + scale_fill_gradient(low = "white", high = "steelblue")

The number of scenes per act tends for vary, as does the number of words per scene. Maybe there is some pattern we can detect by plotting the number of words by scene, by act, and by play. This figure is able to communicate a tremendous amount of information in a very small space. Of course, one must still be walked through it to get the full gist of what is shown. The two wordiest scenes in this subset of the corpus are in Love’s Labour’s Lost Act V, Scene II (the very last scene in the play) and A Winter’s Tale Act IV, Scene IV. I don’t know what happens in these two scenes, but maybe someone who is familiar with the content could identify what makes these two scenes stand out.

shak %>%
  filter(Play == "Hamlet" | Play == "King John" | Play == "The Tempest" | 
           Play == "Cymbeline" | Play == "Measure for measure" | Play == "Timon of Athens" | 
           Play == "Richard III" | Play == "Loves Labours Lost" | Play == "A Winters Tale" | 
           Play == "Othello" | Play == "Romeo and Juliet") %>% 
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Play,act,scene, sort=TRUE) %>%
  transmute(play=Play, act=as.integer(act), scene=as.integer(scene), n=n) %>%
  ggplot(aes(x=scene,y=play)) + 
    geom_tile(aes(fill = n), colour = "white") + 
    scale_fill_gradient(low = "white", high = "red2") +
    scale_x_continuous(breaks=c(0:8)) +
    theme_dark() + 
    facet_wrap(~act, ncol = 5)

A similar style figure could also illustrate where each character has the bulk of their lines. Here, we can see Hamlet is positively verbose in Scene II in Acts II, III, V, but otherwise only marginally wordier than the other players. Moreover, there seems to be the most even distribution of lines (neither very dark nor entirely pale, as colour-coded) in Act I, whereas the other acts are quite skewed toward the few main players.

shak %>%
  filter(Play == "Hamlet") %>% 
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Player,act,scene, sort=TRUE) %>%
  transmute(player=Player, act=as.integer(act), scene=as.integer(scene), n=n) %>%
  ggplot(aes(x=scene,y=reorder(player,n))) + 
    geom_tile(aes(fill = n), colour = "white") + 
    scale_fill_gradient(low = "white", high = "red2") +
    scale_x_continuous(breaks=c(0:8)) +
    theme_dark() + 
    facet_wrap(~act, ncol = 5)

The same type of plot can be used to examine Twelfth Night, which is shorter, more evenly distributed, and much less soliloquy-driven.

shak %>%
  filter(Play == "Twelfth Night") %>% 
  filter(ActSceneLine != "") %>%
  mutate(ActSceneLine2 = ActSceneLine) %>%
  separate(ActSceneLine2, c("act", "scene", "line")) %>%
  count(Player,act,scene, sort=TRUE) %>%
  transmute(player=Player, act=as.integer(act), scene=as.integer(scene), n=n) %>%
  ggplot(aes(x=scene,y=reorder(player,n))) + 
    geom_tile(aes(fill = n), colour = "white") + 
    scale_fill_gradient(low = "white", high = "red2") +
    scale_x_continuous(breaks=c(0:8)) +
    theme_dark() + 
    facet_wrap(~act, ncol = 5)

7 Wrap up

What this all seems to tell us is that we can visualise the structure of the play, separate from their content. Is this useful to you? It will depend on what kinds of research questions you are interested in. However, if any of your questions involve counting things (words, lines, appearances, collocations, ngrams, etc), then it is possible and even likely that visualisations can help communicate that information in a succinct and easily read way.

Of course, counting things is only going to be helpful for some possible questions. But, it’s important to know that not everything that can be counted is a simple number. There are relative frequencies, collocations and n-grams, and changes over “time” (i.e., in different acts, scenes, chapters, by publication year, etc). We can use these relations between numbers to create elegant figures that might or might not communicate numbers specifically (i.e., network graphs). We can then use the figures to explore properties of the text that might not be immediately apparent from close reading.

As a quick (visual) summary of what’s been covered, here is a table of plot types and their (best) uses:

Plot Uses
Bar plot Frequency (relative or absolute)
Histogram Frequency distribution across a scale
Heatmap Frequency/attestation across multiple dimensions
Network graph Collocation (with or without directionality)
Other Plots Uses
Flow chart Temporal order/branching without regard to spacial orientation
Chord diagram Connect concepts by frequency but can be busy or confusing
Choropleth map Map or diagram that uses shading/color to indicate value
Word cloud CAUTION: Pretty but difficult to compare relative sizes
Pie chart DO NOT USE: Difficult to compare relative size of slices
LS0tCnRpdGxlOiAiU2Vzc2lvbiAzOiBUZXh0IGFuZCBjb3JwdXMgZGF0YSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAyCiAgICBkZl9wcmludDogcGFnZWQKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCi0tLQoKVGhlIHB1cnBvc2Ugb2YgdGhpcyBub3RlYm9vayBpcyB0byBkZW1vbnN0cmF0ZSBzb21lIG9mIHdoYXQgaXMgcG9zc2libGUgZm9yIHRoZSBtaW5pbmcgYW5kIHZpc3VhbGlzYXRpb24gb2YgYSB0ZXh0LiBXZSdsbCBnbyB0aHJvdWdoIHNvbWUgb2YgdGhlIGJhc2ljIHRvb2xzIGFuZCBkbyBzb21lIGRhdGEgdmlzdWFsaXNhdGlvbiB0byBkZW1vbnN0cmF0ZSB3aGF0IGlzIHBvc3NpYmxlLiBJZiB0aGlzIGtpbmQgb2YgYW5hbHlzaXMgaXMgbm90IGRpcmVjdGx5IHJlbGV2YW50IHRvIHlvdXIgcmVzZWFyY2gsIGhvcGVmdWxseSB5b3UgY2FuIHVzZSB0aGlzIHRpbWUgdG8gcHJhY3RpY2UgZGlmZmVyZW50IGRhdGEgdmlzdWFsaXNhdGlvbiB0ZWNobmlxdWVzIGFuZCBiZWNvbWUgbW9yZSBmYW1pbGlhciB3aXRoIHNlZWtpbmcgb3V0IGFuc3dlcnMgdG8gcXVlc3Rpb25zIGFib3V0IHByb2dyYW1taW5nIFIgY29kZS4KCiMgU2V0dGluZyB1cAoKSW4gdGhpcyBzZXNzaW9uLCB3ZSdsbCBiZSB1c2luZyBhIGZldyBwYWNrYWdlcywgYnV0IHRoZXknbGwgYmUgaW50cm9kdWNlZCBhcyB3ZSBnby4gRmlyc3RseSwgd2UnbGwgbmVlZCB0byBsb2FkIGluIGB0aWR5dmVyc2VgIGFuZCBbYHRtYF0oaHR0cHM6Ly93d3cudGlkeXRleHRtaW5pbmcuY29tLykgKHRleHQgbWluaW5nKS4gV2Ugd2lsbCBub3QgZXhwbGljaXRseSBiZSB1c2luZyBbYFNub3diYWxsQ2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9Tbm93YmFsbEMvaW5kZXguaHRtbCkgYXQgdGhpcyB0aW1lLCBidXQgaXQgbWF5IGJlIHVzZWZ1bCB0byBrbm93IGFib3V0IGFzIGl0IGhlbHBzIHdpdGggbGVtbWF0aXphdGlvbiBmb3Igd29yZCBmcmVxdWVuY3kgYW5hbHlzaXMgaW4gc2V2ZXJhbCBsYW5ndWFnZXMuIEl0IGlzIG9uZSBvZiBgdG1gJ3MgZGVwZW5kZW5jaWVzLCBhbmQgbWF5IGJlIGRvaW5nIGEgYml0IG9mIGJhY2tncm91bmQgd29yay4gIAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBSIG11c3QgYmUgYXQgbGVhc3QgMy4zLjEgZm9yIGB0bWAgYW5kIGBzbGFtYCB0byB3b3JrLgojIGxpYnJhcnkoIlNub3diYWxsQyIpCmxpYnJhcnkodG0pCgpgYGAKCkJlZm9yZSB3ZSBnZXQgc3RhcnRlZCB3b3JraW5nIHdpdGggdGhlIGFjdHVhbCBkYXRhLCB3ZSdyZSBnb2luZyB0byBjcmVhdGUgYSBjdXN0b20gZnVuY3Rpb24gZm9yIHBsb3R0aW5nIG11bHRpcGxlIGBnZ3Bsb3RgIGdyYXBocyBpbiBhIHNpbmdsZSBpbWFnZS4gVGhpcyBmdW5jdGlvbiB3YXMgY29waWVkIGZyb20gW0Nvb2tib29rIGZvciBSXShodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9NdWx0aXBsZV9ncmFwaHNfb25fb25lX3BhZ2VfKGdncGxvdDIpLykuCgpgYGB7cn0KIyBNdWx0aXBsZSBwbG90IGZ1bmN0aW9uCiMKIyBnZ3Bsb3Qgb2JqZWN0cyBjYW4gYmUgcGFzc2VkIGluIC4uLiwgb3IgdG8gcGxvdGxpc3QgKGFzIGEgbGlzdCBvZiBnZ3Bsb3Qgb2JqZWN0cykKIyAtIGNvbHM6ICAgTnVtYmVyIG9mIGNvbHVtbnMgaW4gbGF5b3V0CiMgLSBsYXlvdXQ6IEEgbWF0cml4IHNwZWNpZnlpbmcgdGhlIGxheW91dC4gSWYgcHJlc2VudCwgJ2NvbHMnIGlzIGlnbm9yZWQuCiMKIyBJZiB0aGUgbGF5b3V0IGlzIHNvbWV0aGluZyBsaWtlIG1hdHJpeChjKDEsMiwzLDMpLCBucm93PTIsIGJ5cm93PVRSVUUpLAojIHRoZW4gcGxvdCAxIHdpbGwgZ28gaW4gdGhlIHVwcGVyIGxlZnQsIDIgd2lsbCBnbyBpbiB0aGUgdXBwZXIgcmlnaHQsIGFuZAojIDMgd2lsbCBnbyBhbGwgdGhlIHdheSBhY3Jvc3MgdGhlIGJvdHRvbS4KIwptdWx0aXBsb3QgPC0gZnVuY3Rpb24oLi4uLCBwbG90bGlzdD1OVUxMLCBmaWxlLCBjb2xzPTEsIGxheW91dD1OVUxMKSB7CiAgbGlicmFyeShncmlkKQoKICAjIE1ha2UgYSBsaXN0IGZyb20gdGhlIC4uLiBhcmd1bWVudHMgYW5kIHBsb3RsaXN0CiAgcGxvdHMgPC0gYyhsaXN0KC4uLiksIHBsb3RsaXN0KQoKICBudW1QbG90cyA9IGxlbmd0aChwbG90cykKCiAgIyBJZiBsYXlvdXQgaXMgTlVMTCwgdGhlbiB1c2UgJ2NvbHMnIHRvIGRldGVybWluZSBsYXlvdXQKICBpZiAoaXMubnVsbChsYXlvdXQpKSB7CiAgICAjIE1ha2UgdGhlIHBhbmVsCiAgICAjIG5jb2w6IE51bWJlciBvZiBjb2x1bW5zIG9mIHBsb3RzCiAgICAjIG5yb3c6IE51bWJlciBvZiByb3dzIG5lZWRlZCwgY2FsY3VsYXRlZCBmcm9tICMgb2YgY29scwogICAgbGF5b3V0IDwtIG1hdHJpeChzZXEoMSwgY29scyAqIGNlaWxpbmcobnVtUGxvdHMvY29scykpLAogICAgICAgICAgICAgICAgICAgIG5jb2wgPSBjb2xzLCBucm93ID0gY2VpbGluZyhudW1QbG90cy9jb2xzKSkKICB9CgogaWYgKG51bVBsb3RzPT0xKSB7CiAgICBwcmludChwbG90c1tbMV1dKQoKICB9IGVsc2UgewogICAgIyBTZXQgdXAgdGhlIHBhZ2UKICAgIGdyaWQubmV3cGFnZSgpCiAgICBwdXNoVmlld3BvcnQodmlld3BvcnQobGF5b3V0ID0gZ3JpZC5sYXlvdXQobnJvdyhsYXlvdXQpLCBuY29sKGxheW91dCkpKSkKCiAgICAjIE1ha2UgZWFjaCBwbG90LCBpbiB0aGUgY29ycmVjdCBsb2NhdGlvbgogICAgZm9yIChpIGluIDE6bnVtUGxvdHMpIHsKICAgICAgIyBHZXQgdGhlIGksaiBtYXRyaXggcG9zaXRpb25zIG9mIHRoZSByZWdpb25zIHRoYXQgY29udGFpbiB0aGlzIHN1YnBsb3QKICAgICAgbWF0Y2hpZHggPC0gYXMuZGF0YS5mcmFtZSh3aGljaChsYXlvdXQgPT0gaSwgYXJyLmluZCA9IFRSVUUpKQoKICAgICAgcHJpbnQocGxvdHNbW2ldXSwgdnAgPSB2aWV3cG9ydChsYXlvdXQucG9zLnJvdyA9IG1hdGNoaWR4JHJvdywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXlvdXQucG9zLmNvbCA9IG1hdGNoaWR4JGNvbCkpCiAgICB9CiAgfQp9CmBgYAoKIyBSZWFkaW5nIGluIGNvcnB1cwoKRm9yIHRoaXMgZGVtb25zdHJhdGlvbiwgSSd2ZSBzZWxlY3RlZCBhIGNvcnB1cyBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzIGFuZCBhZGFwdGVkIHNvbWUgY29kZSBmcm9tIFthIEthZ2dsZSBub3RlYm9va10oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9zaW5kaHVlZS9sb3ZlLWluLXNoYWtlc3BlYXJlP3NjcmlwdFZlcnNpb25JZD0xMTIxMjcwKS4gT25jZSB5b3UgdW5kZXJzdGFuZCB3aGF0IHRoZSBjb2RlIGhlcmUgaXMgZG9pbmcsIGl0IGNhbiBlYXNpbHkgYmUgYWRhcHRlZCB0byBhIGNvcnB1cyBvZiBhbnkgc3RydWN0dXJlIG9yIGNvbnRlbnQuCgpXZSBzaG91bGQgdGFrZSBhIGxvb2sgYXQgaG93IHRoZSBjb3JwdXMgaXMgc3RydWN0dXJlZCBzbyB3ZSBrbm93IHdoYXQgd2UncmUgZGVhbGluZyB3aXRoLgoKYGBge3J9CnNoYWsgPC0gcmVhZC5jc3YoIi4uL2RhdGEvU2hha2VzcGVhcmVfZGF0YS5jc3YiLGhlYWRlciA9IFRSVUUsIGFzLmlzID0gVFJVRSkKYGBgCgpUaGVzZSBhcmUgdGhlIGZpcnN0IHRlbiBsaW5lcyBvZiB0aGUgY29ycHVzLiAgCgpgYGB7cn0Kc2hhawpgYGAKCkVhY2ggY29sdW1uIGlzIGxhYmVsZWQgYW5kIHRoZSBjb250ZW50IG9mIHRoZSBjb2x1bW4gaXMgY29uc2lzdGVudCBmb3IgZWFjaCByb3cgKGFsbCAxMTEzOTYgb2YgdGhlbSEpLiBTb21lIG9mIHRoZSByb3dzIG1heSBub3QgYmUgdXNlZnVsLiBTb21lIGNvbnRhaW4gZW1wdHkgY2VsbHMgKGxhYmVsZWQgYE5BYCkuIFNvbWUgY29udGFpbiBhIGxvdCBvZiBpbmZvcm1hdGlvbiBhbmQgd2UgbWlnaHQgbmVlZCB0byBkbyBzb21lIHByb2Nlc3Npbmcgb24gdGhlbSBiZWZvcmUgd2UgY2FuIHVzZSB0aGUgaW5mb3JtYXRpb24gcXVhbnRpdGF0aXZlbHkgKGZvciBleGFtcGxlIHRoZSBjb2x1bW4gYFBsYXllckxpbmVgKS4KCiMgV29yZCBmcmVxdWVuY3kKClRoZSBmaXJzdCB0aGluZyB3ZSdsbCBsb29rIGF0IGlzIHdvcmQgZnJlcXVlbmN5LCBvciBob3cgb2Z0ZW4gYSBzdHJpbmcgKGluIHRoaXMgY2FzZSAibG92ZSIpIG9jY3VycyBpbiB0aGUgZGF0YSBmcmFtZS4gVG8gZG8gdGhpcywgd2UgbXVzdCBpZGVudGlmeSBldmVyeSB0aW1lIHRoZSB3b3JkICJsb3ZlIiBhcHBlYXJzIGFuZCBoaWdobGlnaHQgaXQgaW4gYSB3YXkgc28gdGhhdCBpdCBjYW4gYmUgY291bnRlZCBiYXNlZCBvbiBkaWZmZXJlbnQgcHJvcGVydGllcyBvZiBpdHMgZW52aXJvbm1lbnQgKGUuZy4sIGJ5IHBsYXksIGJ5IHBsYXllciwgYnkgc2NlbmUsIGV0YykuCgpIZXJlIGFyZSB0aGUgZmlyc3QgMTAgcm93cyBvZiBhIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB0aGUgbnVtYmVyIG9mIHRpbWVzICJsb3ZlIiBhcHBlYXJzIGluIGVhY2ggcGxheS4gSXQncyBiZWVuIHNvcnRlZCBpbiBkZXNjZW5kaW5nIG9yZGVyLCBidXQgZG9lc24ndCBjb250YWluIGFueSBvdGhlciBpbmZvcm1hdGlvbiBhYm91dCB3aGVyZSBhbmQgd2hlbiB0aGUgd29yZCBvY2N1cnMuCgpgYGB7cn0KIyBwbGF5IGxldmVsIHdvcmQgZnJlcXVlbmN5CnBsYXlzIDwtIHVuaXF1ZShzaGFrJFBsYXkpCmxvdmVGcmVxPC1udW1lcmljKCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbGF5cykpewogICAgdGV4dCA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHBhc3RlKHNoYWtbc2hhayRQbGF5PT1wbGF5c1tpXSxdJFBsYXllckxpbmUsY29sbGFwc2U9IiAiKSkpCiAgICB0ZXh0IDwtIHRtX21hcCh0ZXh0LCByZW1vdmVQdW5jdHVhdGlvbikKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsIFBsYWluVGV4dERvY3VtZW50KQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQogICAgCiAgICAjIHN0ZW1taW5nIHRvIG1lcmdlIGFsbCAibG92ZWQiLCAibG92aW5nIiBpbnRvIG9uZSAgIAogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgc3RlbURvY3VtZW50KQogICAgdGRtICA8LSBUZXJtRG9jdW1lbnRNYXRyaXgodGV4dCkKICAgIAogICAgbG92ZUZyZXFbaV08LWFzLm51bWVyaWMoc2xhbTo6cm93X3N1bXModGRtKVsibG92ZSJdKSAjIGBzbGFtYCBtdXN0IGJlIGluc3RhbGxlZCBmb3IgdGhpcyB0byB3b3JrCiAgfQoKbFBsYXkgPC0gZGF0YS5mcmFtZShwbGF5cyxsb3ZlRnJlcSkKbFBsYXkgPC0gbmEub21pdChsUGxheSkKCiMgb3JkZXIgdGhlIHBsYXlzIGJhc2VkIG9uIHRoZSBvY2N1cmVuY2Ugb2YgbG92ZQpsUGxheTwtbFBsYXlbb3JkZXIoLWxQbGF5JGxvdmVGcmVxKSxdCgpsUGxheQpgYGAKCldlIGNhbiBhbHNvIGxvb2sgYXQgd2hpY2ggcGxheWVycyBzYXkgImxvdmUiIHRoZSBtb3N0IG92ZXIgdGhlIGNvdXJzZSBvZiB0aGVpciBhcHBlYXJlbmNlcy4gVGhlc2UgYXJlIG9ubHkgdGhlIHRvcCAxMCBwbGF5ZXJzIHdobyB1c2UgdGhlIHdvcmQgImxvdmUiIG1vc3QuIAoKYGBge3J9CiMgcGxheWVyIGxldmVsIHdvcmQgZnJlcXVlbmN5CnBsYXllcnMgPC0gdW5pcXVlKHNoYWskUGxheWVyKQpsb3ZlRnJlcSA8LSBudW1lcmljKCkKCmZvciAoaSBpbiAxOmxlbmd0aChwbGF5ZXJzKSl7CiAgICB0ZXh0IDwtIENvcnB1cyhWZWN0b3JTb3VyY2UocGFzdGUoc2hha1tzaGFrJFBsYXllcj09cGxheWVyc1tpXSxdJFBsYXllckxpbmUsY29sbGFwc2U9IiAiKSkpCiAgICB0ZXh0IDwtIHRtX21hcCh0ZXh0LCByZW1vdmVQdW5jdHVhdGlvbikKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsIFBsYWluVGV4dERvY3VtZW50KQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgc3RlbURvY3VtZW50KQogICAgCiAgICB0ZG0gIDwtIFRlcm1Eb2N1bWVudE1hdHJpeCh0ZXh0KQogICAgCiAgICBsb3ZlRnJlcVtpXSA8LSBhcy5udW1lcmljKHNsYW06OnJvd19zdW1zKHRkbSlbImxvdmUiXSkKICB9CgpsUGxheWVyIDwtIGRhdGEuZnJhbWUocGxheWVycyxsb3ZlRnJlcSkKbFBsYXllciA8LSBuYS5vbWl0KGxQbGF5ZXIpCiNvcmRlcgpsUGxheWVyIDwtIGxQbGF5ZXJbb3JkZXIoLWxQbGF5ZXIkbG92ZUZyZXEpLF0KCmxQbGF5ZXIKYGBgCgojIyBFeHBsb3Jpbmcgc29tZW9uZSBlbHNlJ3MgY29kZQoKTGV0J3MgZ28gdGhyb3VnaCB0aGUgZm9yLWxvb3AgaW4gbW9yZSBkZXRhaWwgdG8gdW5kZXJzdGFuZCB3aGF0IGl0J3MgZG9pbmcuCgoqKkhvdyBtYW55IHBsYXllcnMgYXJlIGluIHRoaXMgdmVjdG9yPyoqIChBbnN3ZXI6IGByIGxlbmd0aChwbGF5ZXJzKWApICAKVGhhdCdzIHRoZSBudW1iZXIgb2YgdGltZXMgdGhpcyBmb3ItbG9vcCB3aWxsIGN5Y2xlLCBlYWNoIHRpbWUgaW5jcmVtZW50aW5nIHRoZSBsb29wIG51bWJlciBieSAxLgpgYGAKZm9yIChpIGluIDE6bGVuZ3RoKHBsYXllcnMpKXsKYGBgCgpUaGUgZmlyc3QgbGluZSBvZiB0aGUgbG9vcCB1c2VzIGBDb3JwdXMoKWAgYW5kIGBWZWN0b3JTb3VyY2UoKWAuICoqV2hhdCBkbyB0aGVzZSBmdW5jdGlvbnMgZG8/KiogIApXZSBjYW4gY2hlY2sgdGhlIEhlbHAgZmlsZXMgYnkgdHlwaW5nIGA/VmVjdG9yU291cmNlKClgLiBJdCBsb29rcyBsaWtlIGJvdGggZGVhbCB3aXRoIHRoZSBzdHJ1Y3R1cmUgYW5kIG1ldGFkYXRhIG9mIGEgY29ycHVzLiBJbXBvcnRhbnRseSwgd2UgY2FuIHNlZSBgW2ldYCBpbiB0aGUgY29kZSB0aG91Z2guIFRoaXMgaXMgdGhlIHNhbWUgYGlgIGZyb20gdGhlIHByZXZpb3VzIGxpbmUsIHdoaWNoIGN5Y2xlcyB0aHJvdWdoIGVhY2ggb2YgdGhlIHBsYXllcnMgaW4gdGhlIHZlY3Rvci4gIAoqKldoYXQgZG9lcyB0aGF0IHRlbGwgdXMgYWJvdXQgdGhlIGZpcnN0IHBhc3MgdGhyb3VnaCB0aGlzIGxvb3A/KioKYGBgCnRleHQgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShwYXN0ZShzaGFrW3NoYWskUGxheWVyPT1wbGF5ZXJzW2ldLF0kUGxheWVyTGluZSxjb2xsYXBzZT0iICIpKSkKYGBgCgpUaGUgbmV4dCBmZXcgbGluZXMgYWxsIHVzZSB0aGUgZnVuY3Rpb24gYHRtX21hcCgpYCwgd2hpY2ggcGVyZm9ybXMgYSB0cmFuc2Zvcm1hdGlvbiBvbiB0aGUgY29ycHVzLiBJbiB0aGlzIGNhc2UsIHRoZSBjb3JwdXMgaXMgdGhlIHN1YnNldCB3ZSd2ZSBjYWxsZWQgYHRleHRgIGluIHRoZSBhYm92ZSBsaW5lLiBKdWRnaW5nIGZyb20gdGhlIHN5bnRheCwgaXQgYXBwZWFycyB0aGF0IHRoZXkgcmVzcGVjdGl2ZWx5IHJlbW92ZSBwdW5jdHVhdGlvbiBmcm9tIHRoZSB0ZXh0LCBjb252ZXJ0IGl0IHRvIGEgcGxhaW4gdGV4dCBkb2N1bWVudCwgcmVtb3ZlIEVuZ2xpc2ggc3RvcCB3b3JkcywgYW5kIG1lcmdlIGFsbCB3b3JkcyB3aXRoIHRoZSBzYW1lIHN0ZW1zIG9yIGxlbW1hcyAoaS5lLiwgImxvdmUiLCAibG92aW5nIiwgImxvdmVkIiwgZXRjKS4KYGBgCnRleHQgPC0gdG1fbWFwKHRleHQsIHJlbW92ZVB1bmN0dWF0aW9uKQp0ZXh0IDwtIHRtX21hcCh0ZXh0LCBQbGFpblRleHREb2N1bWVudCkKdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQp0ZXh0IDwtIHRtX21hcCh0ZXh0LCBzdGVtRG9jdW1lbnQpCmBgYAoKVGhpcyBsaW5lIHR1cm5zIHRoZSByZXN1bHRpbmcgdmVjdG9yIGludG8gYSB0ZXJtLWRvY3VtZW50IG1hdHJpeCwgd2hpY2ggaXMgYmFzaWNhbGx5IGEgd2F5IG9mIGlkZW50aWZ5aW5nIHdoZXJlIGVhY2ggd29yZCBvY2N1cnMgbnVtZXJpY2FsbHksIHdoaWNoIHdpbGwgYWxsb3cgdXMgdG8gKmNvdW50KiB0aGUgd29yZHMgaW4gdGhlIG5leHQgbGluZS4gCmBgYAp0ZG0gIDwtIFRlcm1Eb2N1bWVudE1hdHJpeCh0ZXh0KQpgYGAKQSBzaW1wbGUgVERNIG1heSBsb29rIGxpa2UgdGhpczoKCgsgfCBlbnRlciB8IGV4aXQgfCBoYW1sZXQgfCBvcGhlbGlhIHwgYW5kCi0tfC0tfC0tfC0tfC0tfC0tCnRleHRfMSB8IDEgfCAwIHwgMSB8IDAgfCAwCnRleHRfMiB8IDEgfCAwIHwgMCB8IDEgfCAwCnRleHRfMyB8IDAgfCAxIHwgMSB8IDEgfCAxCgoqKldoYXQgbWlnaHQgdGhlc2UgdGhyZWUgInRleHRzIiBzYXk/KioKClRoZSBsYXN0IGxpbmUgaGFzIHNldmVyYWwgdGhpbmdzIGdvaW5nIG9uLiBXZSBjYW4gZmlndXJlIG91dCB3aGF0IGVhY2ggZnVuY3Rpb24gZG9lcyBieSB0cnlpbmcgaXQgb24gaXRzIG93bi4KYGBgCmxvdmVGcmVxW2ldIDwtIGFzLm51bWVyaWMoc2xhbTo6cm93X3N1bXModGRtKVsibG92ZSJdKQpgYGAKRmlyc3QsIHdlIGNhbiBzZWUgd2hhdCBgdGRtYCBsb29rcyBsaWtlIHdoZW4gYGkgPSAxYDoKYGBge3J9CnRkbQpgYGAKCkhtbW0sIHRoYXQgZG9lc24ndCBsb29rIGludGVycHJldGFibGUgYnkgYSBodW1hbi4gTWF5YmUgYHJvd19zdW1zKClgIGZyb20gdGhlIHBhY2thZ2UgYHNsYW1gIHdpbGwgaGVscD8KYGBge3J9CnNsYW06OnJvd19zdW1zKHRkbSkKYGBgCgpBaCBoYSEgVGhhdCBsb29rcyBtb3JlIGludGVsbGlnaWJsZS4gKipXaGljaCBwbGF5ZXIgaXMgdGhpcyBmb3I/KiogIAooQW5zd2VyID0gYHIgcGxheWVyc1sxXWA7IGlmIHlvdSB3YW50IHRvIHNlZSB3aGF0IGRpZmZlcmVudCBwbGF5ZXJzIGFyZSBkb2luZywgeW91IGNhbiBjaGFuZ2UgdGhlIHZhbHVlIG9mIGBpYCBieSBoYW5kIGFuZCByZS1ydW4gdGhlIGNvZGUuKQoKU28gd2hhdCBpcyBgWyJsb3ZlIl1gIGRvaW5nIGluIHRoZSBjb2RlPwpgYGB7cn0Kc2xhbTo6cm93X3N1bXModGRtKVsibG92ZSJdCmBgYAoKQWgsICJsb3ZlIiBkb2Vzbid0IGV4aXN0IGluIHRoaXMgcm93ICh3aGVyZSBgaSA9IDFgKS4gTGV0J3Mgc2VlIHdoYXQgaXQgd291bGQgbG9vayBsaWtlIGlmIHdlIHdlcmUgYWN0dWFsbHkgaW50ZXJlc3RlZCBpbiAiZW50ZXIiOgpgYGB7cn0Kc2xhbTo6cm93X3N1bXModGRtKVsiZW50ZXIiXQpgYGAKClRoZXJlIGFyZSB0d28gZWxlbWVudHMgdGhhdCBhcmUgYmVpbmcgcHJpbnRlZCBvdXQgaGVyZTogdGhlIHdvcmQgKCJlbnRlciIpIGFuZCB0aGUgc3VtICgzKS4gVGhhdCdzIHdoYXQgYGFzLm51bWVyaWMoKWAgd2lsbCBoZWxwIHVzIHdpdGg6CmBgYHtyfQphcy5udW1lcmljKHNsYW06OnJvd19zdW1zKHRkbSlbImVudGVyIl0pCmBgYAoKVGhpcyBtZWFucyB0aGF0IHRoZSBzdW0gb2Ygb2NjdXJyZW5jZXMgb2YgImxvdmUiIGlzIG5vdyBlbnRlcmVkIGludG8gaW5kZXggYFtpXWAgaW4gdGhlIHZlY3RvciBgbG92ZUZyZXFgLCBhbmQgdGhlIGxvb3AgYmVnaW5zIGFnYWluIHVudGlsIGBpID0gbGVuZ3RoKHBsYXllcnMpYC4KCkZpbmFsbHksIHdlIGNsb3NlIG9mZiB0aGUgZm9yLWxvb3AuCmBgYAp9CmBgYAoKQ2FuIHlvdSBmaWd1cmUgb3V0IGhvdyB0byB1c2UgdGhlIGNvZGUgYWJvdmUgdG8gY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBmb2xsb3dpbmcgdHdvIGNvbmRpdGlvbnM/OgoKKiBBIGxpc3Qgb2YgKipwbGF5ZXJzKioKKiBPcmRlcmVkIGJ5IGZyZXF1ZW5jeSBvZiB0aGUgd29yZCAqKmRlYXRoKioKCmBgYHtyfQojIGNyZWF0ZSB0aGUgdmVjdG9ycyB5b3UgbmVlZAojIGNyZWF0ZSB0aGUgZm9yLWxvb3AgdG8gY2FsY3VsYXRlIGZyZXF1ZW5jeQojIGNyZWF0ZSB0aGUgZGF0YSBmcmFtZQojIG9yZGVyIHRoZSBkYXRhIGZyYW1lIGZyb20gbW9zdCB0byBsZWFzdCBmcmVxdWVudApgYGAKCjxiaWc+KHBvc3NpYmxlIHNvbHV0aW9uIGJlbG93KTwvYmlnPiAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCgpgYGB7cn0KIyBjcmVhdGUgdGhlIHZlY3RvcnMgeW91IG5lZWQKIyBwbGF5ZXJzIDwtIHVuaXF1ZShzaGFrJFBsYXllcikgIyA8LS0gd2UgYWxyZWFkeSBtYWRlIHRoaXMgb25lIGJlZm9yZSwgYnV0IHdlIGNhbiBkbyBpdCBhZ2FpbiB0b28KZGVhdGhGcmVxIDwtIG51bWVyaWMoKSAjIDwtLSBjcmVhdGUgYSB2ZWN0b3Igb2YgY2xhc3MgIm51bWVyaWMiCgojIGNyZWF0ZSB0aGUgZm9yLWxvb3AgdG8gY2FsY3VsYXRlIGZyZXF1ZW5jeQpmb3IgKGkgaW4gMTpsZW5ndGgocGxheWVycykpewogICAgdGV4dCA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHBhc3RlKHNoYWtbc2hhayRQbGF5ZXI9PXBsYXllcnNbaV0sXSRQbGF5ZXJMaW5lLGNvbGxhcHNlPSIgIikpKQogICAgdGV4dCA8LSB0bV9tYXAodGV4dCwgcmVtb3ZlUHVuY3R1YXRpb24pCiAgICB0ZXh0IDwtIHRtX21hcCh0ZXh0LCBQbGFpblRleHREb2N1bWVudCkKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoJ2VuZ2xpc2gnKSkKICAgIHRleHQgPC0gdG1fbWFwKHRleHQsc3RlbURvY3VtZW50KQogICAgCiAgICB0ZG0gIDwtIFRlcm1Eb2N1bWVudE1hdHJpeCh0ZXh0KQogICAgCiAgICBkZWF0aEZyZXFbaV0gPC0gYXMubnVtZXJpYyhzbGFtOjpyb3dfc3Vtcyh0ZG0pWyJkZWF0aCJdKQogIH0KCiMgY3JlYXRlIHRoZSBkYXRhIGZyYW1lCmRQbGF5ZXIgPC0gZGF0YS5mcmFtZShwbGF5ZXJzLGRlYXRoRnJlcSkKZFBsYXllciA8LSBuYS5vbWl0KGRQbGF5ZXIpCiMgb3JkZXIgdGhlIGRhdGEgZnJhbWUgZnJvbSBtb3N0IHRvIGxlYXN0IGZyZXF1ZW50CmRQbGF5ZXIgPC0gZFBsYXllcltvcmRlcigtZFBsYXllciRkZWF0aEZyZXEpLF0KZFBsYXllcgpgYGAKCiMgVmlzdWFsaXNpbmcgYSBjb3JwdXMKCk1heWJlIHlvdSBhcmUgY3VyaW91cyBob3cgdGhlIGxvbmdlciBhbmQgc2hvcnRlciBwbGF5cyBjb21wYXJlIGluIHdvcmQgZnJlcXVlbmN5LiBJbnN0ZWFkIG9mIGhhbmQtY291bnRpbmcgZWFjaCwgd2UgY2FuIGdyYXBoIGFuZCBvcmRlciB0aGVtLiBCYXNlZCBvbiB0aGlzIGdyYXBoLCB5b3UgZG9uJ3QgbmVlZCB0byBrbm93IGV4YWN0bHkgaG93IGxvbmcgZWFjaCBpcywgYnV0IHlvdSBjYW4gc2VlIHRoYXQgKk90aGVsbG8qIGlzIG11Y2ggbG9uZ2VyIHRoYW4gKkxvdmVzIExhYm91cnMgTG9zdCosIHdoaWNoIGNhbiBpbmZvcm0gaG93IHlvdSBhcHByb2FjaCB0aGUgY29tcGFyaXNvbiBsYXRlciBvbi4KCmBgYHtyfQpzaGFrICU+JQogICMgYFBsYXlgIGlzIHRoZSBmYWN0b3Igd2UgYXJlIGludGVyZXN0ZWQgaW4KICBncm91cF9ieShQbGF5KSAlPiUgCiAgIyBzdW1tYXJpc2UgdGhlIGNvbnRlbnQgb2YgYFBsYXlgIGJ5IGNvdW50aW5nIGhvdyBtYW55IGluc3RhbmNlcyB0aGVyZSBhcmUgb2YgZWFjaCAoYG5gKQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUgCiAgIyByZW9yZGVyKCkgYWxsb3dzIHVzIHRvIHNvcnQgdGhlbSBncmVhdGVzdCB0byBsZWFzdAogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKFBsYXksIG4pLHk9bikpICsgCiAgICAjIHN0YXQ9ImlkZW50aXR5IiBpcyBjcnVjaWFsIGZvciBnZ3Bsb3QgdG8ga25vdyBlYWNoIGBuYCBpcyB0aGUgbGVuZ3RoIG9mIHRoZSBiYXIKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKyAKICAgICMgdGhpcyB3YXksIHRoZSBwbGF5IG5hbWVzIHdpbGwgYmUgaG9yaXpvbnRhbCBhbmQgZnVsbHkgZGlzcGxheWVkCiAgICBjb29yZF9mbGlwKCkgKyAKICAgIGdndGl0bGUoIkxlbmd0aCBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzIikgKwogICAgeGxhYigiUGxheSIpICsKICAgIHlsYWIoIk51bWJlciBvZiBsaW5lcyIpCmBgYAoKIyMgUGVyY2VwdGlvbiBvZiBmcmVxdWVuY3kKCldpdGhpbiBhIHNpbmdsZSBwbGF5LCBtYXliZSB3ZSB3YW50IHRvIGtub3cgd2hpY2ggY2hhcmFjdGVycyBhcmUgdGhlIGNoYXR0aWVzdC4gV2UgY2FuIHZpc3VhbGlzZSB0aGUgbnVtYmVyIG9mIGxpbmVzIG9mIHRleHQgcGVyIGNoYXJhY3RlciB0byBnZXQgYSBzZW5zZSBvZiB3aG8gaXMgZG9taW5hdGluZyB0aGUgc3RhZ2UuCgpgYGB7cn0Kc2hhayAlPiUKICBmaWx0ZXIoUGxheSA9PSAiSGFtbGV0IikgJT4lICMgbGltaXQgb3VyIGRhdGFzZXQgdG8gb25seSB0aGUgcGxheSAiSGFtbGV0IgogIGdyb3VwX2J5KFBsYXllcikgJT4lCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKFBsYXllciwgbikseT1uKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiU3BlZWNoIGluIEhhbWxldCIpICsKICAgIHhsYWIoIlBsYXllciIpICsKICAgIHlsYWIoIk51bWJlciBvZiBsaW5lcyIpICMrCiAgICAjc2NhbGVfeV9sb2cxMCgpCmBgYAoKV2UgY2FuIGFsc28gbG9vayBhY3Jvc3MgcGxheXMgZm9yIGZyZXF1ZW5jeS4gQnkgY29tcGFyaW5nIHdoaWNoIHBsYXlzIGhhdmUgdGhlIHdvcmQgImxvdmUiIHRoZSBtb3N0IG9mdGVuLCB3ZSBtaWdodCBiZSBhYmxlIHRvIGdyb3VwIHRoZW0gKHBlcmNlcHR1YWxseSkgaW50byBwbGF5cyAqYWJvdXQgbG92ZSogYW5kIHRob3NlIHRoYXQgYXJlIG5vdC4gTWF5YmU/CgpgYGB7cn0KbFBsYXkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIocGxheXMsIGxvdmVGcmVxKSx5PWxvdmVGcmVxKSkgKwogICAgZ2VvbV9iYXIoYWVzKCksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiTG92ZSBpbiBlYWNoIHBsYXkiKSArCiAgICB4bGFiKCJQbGF5IikgKwogICAgeWxhYigiZnJlcXVlbmN5IG9mIHRoZSB3b3JkICdsb3ZlJyIpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyBDb21wYXJpbmcgYWNyb3NzIHN1YnNldHMKCk9uZSB0aGluZyB0aGF0IGdyYXBocyBjYW4gZG8gdmVyeSBlYXNpbHkgaXMgZ2l2ZSB5b3UgYSB3YXkgdG8gaWRlbnRpZnkgdHJlbmRzIHdoZW4geW91IHNvcnQgZXZlbnRzIChlLmcuLCBwbGF5cykgaW50byBtdWx0aXBsZSBkaWZmZXJlbnQgY2F0ZWdvcmllcy4gRm9yIGluc3RhbmNlLCB0aGUgZnJlcXVlbmN5IGdyYXBoIGFib3ZlIGlzIGludGVyZXN0aW5nLCBidXQgdGhlcmUgYXJlIHNvIG1hbnkgcGxheXMgYW5kIGFzIGEgbm9uLWV4cGVydCwgSSBjYW4ndCB0ZWxsIHlvdSB3aGF0IGVhY2ggaXMgYWJvdXQsIHdoYXQgc3R5bGUgaXQgaXMgd3JpdHRlbiBpbiwgb3Igd2hldGhlciBJJ2QgZXhwZWN0IGl0IHRvIGJlIGFib3V0ICJsb3ZlIiBvciBub3QuIFNvLCB3ZSBjYW4gYWRkIGFub3RoZXIgZGltZW5zaW9uIG9mIGluZm9ybWF0aW9uLgoKYGBge3J9CmxQbGF5Q2F0IDwtIGxQbGF5CgojIGNyZWF0ZSBib29sZWFuIHZlY3RvcnMgZm9yIGZvdXIgcGxheSBjYXRlZ29yaWVzCmNvbWVkaWVzIDwtIGMoIkEgQ29tZWR5IG9mIEVycm9ycyIsCiAgICAgICAgICAgICJBcyB5b3UgbGlrZSBpdCIsCiAgICAgICAgICAgICJBbGxzIHdlbGwgdGhhdCBlbmRzIHdlbGwiLAogICAgICAgICAgICAiTG92ZXMgTGFib3VycyBMb3N0IiwKICAgICAgICAgICAgIk1lYXN1cmUgZm9yIG1lYXN1cmUiLAogICAgICAgICAgICAiTWVyY2hhbnQgb2YgVmVuaWNlIiwKICAgICAgICAgICAgIk1lcnJ5IFdpdmVzIG9mIFdpbmRzb3IiLAogICAgICAgICAgICAiQSBNaWRzdW1tZXIgbmlnaHRzIGRyZWFtIiwKICAgICAgICAgICAgIk11Y2ggQWRvIGFib3V0IG5vdGhpbmciLAogICAgICAgICAgICAiVGFtaW5nIG9mIHRoZSBTaHJldyIsCiAgICAgICAgICAgICJUd2VsZnRoIE5pZ2h0IiwKICAgICAgICAgICAgIlR3byBHZW50bGVtZW4gb2YgVmVyb25hIikKcm9tYW5jZXMgPC0gYygiUGVyaWNsZXMiLAogICAgICAgICAgICAgIkN5bWJlbGluZSIsCiAgICAgICAgICAgICAiQSBXaW50ZXJzIFRhbGUiLAogICAgICAgICAgICAgIlRoZSBUZW1wZXN0IikKaGlzdG9yaWVzIDwtIGMoIktpbmcgSm9obiIsCiAgICAgICAgICAgICAiUmljaGFyZCBJSSIsCiAgICAgICAgICAgICAiUmljaGFyZCBJSUkiLAogICAgICAgICAgICAgIkhlbnJ5IElWIiwKICAgICAgICAgICAgICJIZW5yeSBWIiwKICAgICAgICAgICAgICJIZW5yeSBWSSBQYXJ0IDEiLAogICAgICAgICAgICAgIkhlbnJ5IFZJIFBhcnQgMiIsCiAgICAgICAgICAgICAiSGVucnkgVkkgUGFydCAzIiwKICAgICAgICAgICAgICJIZW5yeSBWSUlJIiwKICAgICAgICAgICAgICJDb3Jpb2xhbnVzIiwKICAgICAgICAgICAgICJKdWxpdXMgQ2Flc2FyIiwKICAgICAgICAgICAgICJBbnRvbnkgYW5kIENsZW9wYXRyYSIsCiAgICAgICAgICAgICAiS2luZyBMZWFyIiwKICAgICAgICAgICAgICJtYWNiZXRoIikKdHJhZ2VkaWVzIDwtIGMoIlRpdHVzIEFuZHJvbmljdXMiLAogICAgICAgICAgICAgIlJvbWVvIGFuZCBKdWxpZXQiLAogICAgICAgICAgICAgIkhhbWxldCIsCiAgICAgICAgICAgICAiVHJvaWx1cyBhbmQgQ3Jlc3NpZGEiLAogICAgICAgICAgICAgIk90aGVsbG8iLAogICAgICAgICAgICAgIlRpbW9uIG9mIEF0aGVucyIpCiMgY3JlYXRlIGJvb2xlYW4gdmVjdG9ycyBmb3IgZm91ciBwbGF5IGNhdGVnb3JpZXMKY29tZWR5IDwtIGxQbGF5Q2F0JHBsYXlzICVpbiUgY29tZWRpZXMKcm9tYW5jZSA8LSBsUGxheUNhdCRwbGF5cyAlaW4lIHJvbWFuY2VzCmhpc3RvcmllcyA8LSBsUGxheUNhdCRwbGF5cyAlaW4lIGhpc3Rvcmllcwp0cmFnZWR5IDwtIGxQbGF5Q2F0JHBsYXlzICVpbiUgdHJhZ2VkaWVzCgojIGNyZWF0ZSBuZXcgY29sdW1uIHdpdGggdGhlc2UgZm91ciBjYXRlZ29yaWVzCmxQbGF5Q2F0IDwtIGxQbGF5ICU+JQogIG11dGF0ZShjYXRlZ29yeSA9IGNhc2Vfd2hlbihjb21lZHkgPT0gVFJVRSB+ICJjb21lZHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb21hbmNlID09IFRSVUUgfiAicm9tYW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpc3RvcnkgPT0gVFJVRSB+ICJoaXN0b3J5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhZ2VkeSA9PSBUUlVFIH4gInRyYWdlZHkiKSkKYGBgCgpJbiB0aGUgZm9sbG93aW5nIGdyYXBoLCBlYWNoIGNvbG9yIHJlcHJlc2VudHMgYSBkaWZmZXJlbnQgY2F0ZWdvcnkgKGFzIGRldGVybWluZWQgYnkgV2lraXBlZGlhJ3MgW0ZpcnN0IEZvbGlvXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9GaXJzdF9Gb2xpbykgcGFnZSwgcGx1cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgImxhdGUgcm9tYW5jZXMiKS4gTm93LCB3ZSBjYW4gc2VlIGlmIHRoZXJlIGFyZSB0cmVuZHMgZm9yIGRpZmZlcmVudCBjYXRlZ29yaWVzIHRvIG1lbnRpb24gImxvdmUiIG1vcmUgb3IgbGVzcyB0aGFuIHRoZSBvdGhlcnMuCgpgYGBge3J9CmxQbGF5Q2F0ICU+JQogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKHBsYXlzLCBsb3ZlRnJlcSkseT1sb3ZlRnJlcSkpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsPWNhdGVnb3J5KSxzdGF0PSJpZGVudGl0eSIpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBnZ3RpdGxlKCJMb3ZlIGluIGVhY2ggcGxheSIpICsKIyAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgICB4bGFiKCJQbGF5IikgKwogICAgeWxhYigiZnJlcXVlbmN5IG9mIHRoZSB3b3JkICdsb3ZlJyIpCmBgYGAKCkl0IHNlZW1zIHRvIG1lIHRoYXQgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6I0Y4NzY2RCI+Y29tZWRpZXM8L3NwYW4+IGFuZCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojQzc3Q0ZGIj50cmFnZWRpZXM8L3NwYW4+IGRpc2N1c3MgImxvdmUiIHRoZSBtb3N0LCB3aGVyZWFzIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiM3Q0FFMDAiPmhpc3Rvcmllczwvc3Bhbj4gYW5kIHRoZSBsYXRlIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiMwMEJGQzQiPnJvbWFuY2VzPC9zcGFuPiBkaXNjdXNzIGl0IHRoZSBsZWFzdC4gSXMgdGhpcyBpbnR1aXRpdmU/IE1heWJlLiBCdXQgdGhlcmUncyBhIHByb2JsZW0uICpBIENvbWVkeSBvZiBFcnJvcnMqIGhhcyB0aGUgZmV3ZXN0IG1lbnRpb25zIG9mICJsb3ZlIiwgYnV0IGl0J3MgYWxzbyB0aGUgc2hvcnRlc3QgcGxheSwgc28gaXQgaGFzIHRoZSBmZXdlc3Qgd29yZHMgb3ZlcmFsbC4gV2hhdCB3ZSByZWFsbHkgd2FudCB0byBzZWUgaXMgdGhlIHByb3BvcnRpb24gb2YgImxvdmUiLWZyZXF1ZW5jeSBwZXIgcGxheSwgbm90IHRoZSByYXcgY291bnRzLiBUbyBkbyB0aGF0LCB3ZSBoYXZlIHRvIGFkZCBpbiB0aGUgdG90YWwgbGVuZ3RoIG9mIGVhY2ggcGxheSB0byB0aGUgZGF0YSBmcmFtZSwgYnV0IHdlJ3ZlIGxvc3QgdGhhdCBpbmZvcm1hdGlvbiBpbiBgbFBsYXlDYXRgIGFuZCBgbFBsYXlgLiAqKkhvdyBtaWdodCB5b3UgYWRkIHRoYXQgaW5mb3JtYXRpb24gaW4gdG8gdGhlIGBsUGxheUNhdGAgZGF0YWZyYW1lPyoqCgpgYGB7cn0KCmBgYAoKPGJpZz4ocG9zc2libGUgc29sdXRpb24gYmVsb3cpPC9iaWc+ICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoLICAKCyAgCgsgIAoKCgpgYGB7cn0KIyBjcmVhdGUgbmV3IGRhdGEgZnJhbWUgZm9yIGxlbmd0aHMgb2YgcGxheXMKcGxheUxlbmd0aCA8LSBzaGFrICU+JQogIGdyb3VwX2J5KFBsYXkpICU+JQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUKICB0cmFuc211dGUocGxheXM9UGxheSxsZW5ndGg9bikgIyByZW5hbWUgY29sdW1ucyB0aGF0IHdlIHdhbnQsIHdoaWNoIGhhcHBlbnMgdG8gYmUgYm90aCBvZiB0aGVtCgojIGFkZCBjb2x1bW4gdG8gbFBsYXlDYXQKbFBsYXlDYXRMZW4gPC0gbFBsYXlDYXQgJT4lCiAgbGVmdF9qb2luKC4scGxheUxlbmd0aCkKYGBgCmByIGxQbGF5Q2F0TGVuYAoKTm93IHdlIGNhbiB0YWtlIGEgbG9vayB0byBzZWUgaG93IHJhdyBhbmQgcHJvcG9ydGlvbmFsIGdyYXBocyBjb21wYXJlLgoKYGBge3J9CmxQbGF5Q2F0TGVuICU+JQogIG11dGF0ZShwcm9wb3J0aW9uID0gbG92ZUZyZXEvbGVuZ3RoKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cmVvcmRlcihwbGF5cywgcHJvcG9ydGlvbikseT1wcm9wb3J0aW9uKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9Y2F0ZWdvcnkpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIkxvdmUgaW4gZWFjaCBwbGF5IikgKwojICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICAgIHhsYWIoIlBsYXkiKSArCiAgICB5bGFiKCJwcm9wb3J0aW9uYWwgZnJlcXVlbmN5IG9mIHRoZSB3b3JkICdsb3ZlJyIpCgpgYGAKCk5vdCBhIHdob2xlIGxvdCBoYXMgY2hhbmdlZCwgYnV0IEkgdGhpbmsgdGhlIGRpc3RyaWJ1dGlvbiBvZiA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojRjg3NjZEIj5jb21lZGllczwvc3Bhbj4gYW5kIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNDNzdDRkYiPnRyYWdlZGllczwvc3Bhbj4gaXMgZXZlbiBtb3JlIHByb25vdW5jZWQuIEFuZCwgd2UgaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0ICpBIENvbWVkeSBvZiBFcnJvcnMqLCB3aGljaCBpcyBzdGlsbCB2ZXJ5IGNsb3NlIHRvIHRoZSBib3R0b20gb2YgdGhlIGdyYXBoLiBOb3QgZXZlcnkgY29tZWR5IGlzIGFib3V0IGxvdmUsIGl0IHNlZW1zLgoKRmluYWxseSwgd2UgY2FuIGdlbmVyYXRlIHRoZXNlIHNhbWUgdHlwZXMgb2YgZ3JhcGhzIGZvciBkaWZmZXJlbnQgc3ViZ3JvdXBzLCB0b28uIFdlJ2xsIGhhdmUgdG8gYWRkIGluIGAkY2F0ZWdvcnlgIHRvIGBzaGFrYCBhcyB3ZWxsLCBidXQgd2UgYWxyZWFkeSBoYXZlIHRoZSB2ZWN0b3JzIGZvciBlYWNoIGxldmVsIG9mIGBjYXRlZ29yeWAsIHNvIGl0J3MgcHJldHR5IGVhc3kgdG8gZG8uCgpgYGB7cn0KIyBjcmVhdGUgYm9vbGVhbiB2ZWN0b3JzIGZvciBmb3VyIHBsYXkgY2F0ZWdvcmllcyBpbiBgc2hha2AKY29tZWR5ICA8LSBzaGFrJFBsYXkgJWluJSBjb21lZGllcwpyb21hbmNlIDwtIHNoYWskUGxheSAlaW4lIHJvbWFuY2VzCmhpc3RvcnkgPC0gc2hhayRQbGF5ICVpbiUgaGlzdG9yaWVzCnRyYWdlZHkgPC0gc2hhayRQbGF5ICVpbiUgdHJhZ2VkaWVzCgojIGNyZWF0ZSBuZXcgY29sdW1uIHdpdGggdGhlc2UgZm91ciBjYXRlZ29yaWVzCnNoYWtDYXQgPC0gc2hhayAlPiUKICBtdXRhdGUoY2F0ZWdvcnkgPSBjYXNlX3doZW4oY29tZWR5ID09IFRSVUUgfiAiY29tZWR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9tYW5jZSA9PSBUUlVFIH4gInJvbWFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoaXN0b3J5ID09IFRSVUUgfiAiaGlzdG9yeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWdlZHkgPT0gVFJVRSB+ICJ0cmFnZWR5IikpCmBgYApgciBzaGFrQ2F0YAoKSGVyZSdzIG9uZSBleGFtcGxlLCB3aGVyZSB3ZSBsb29rIGF0IHRoZSBudW1iZXIgb2YgbGluZXMgZWFjaCBwbGF5ZXIgaGFzLCBmb2N1c2luZyBvbmx5IG9uIHBsYXllcnMgd2hvIGhhdmUgZ3JlYXRlciB0aGFuIDcwMCBsaW5lcy4gV2UgY2FuIGFsc28gc2VlIGlmIHRoZXJlIGFyZSBhbnkgdHJlbmRzIGluIHRoZXNlIHRvcCBzcGVha2VycyBieSBwbGF5IGNhdGVnb3J5LiBJdCBzZWVtcyB0byBtZSB0aGF0IHRoZSA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojN0NBRTAwIj5oaXN0b3JpZXM8L3NwYW4+IGRvbWluYXRlLCBidXQgSGFtbGV0IGFuZCBJYWdvIGRvbWluYXRlIHRoZSBzY2VuZSAoc28gdG8gc3BlYWspLgoKYGBge3J9CnNoYWtDYXQgJT4lCiAgZ3JvdXBfYnkoUGxheSxQbGF5ZXIsY2F0ZWdvcnkpICU+JQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUKICBmaWx0ZXIobiA+IDcwMCkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIoUGxheWVyLCBuKSx5PW4pKSArCiAgICBnZW9tX2JhcihhZXMoZmlsbD1jYXRlZ29yeSksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiTnVtYmVyIG9mIGxpbmVzIGJ5IGNoYXJhY3RlciIpICsKIyAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgICB4bGFiKCJQbGF5ZXIiKSArCiAgICB5bGFiKCJOdW1iZXIgb2YgbGluZXMiKQpgYGAKCklzIHRoaXMgYmVjYXVzZSA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojN0NBRTAwIj5oaXN0b3JpZXM8L3NwYW4+IGFuZCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojQzc3Q0ZGIj50cmFnZWRpZXM8L3NwYW4+IHRlbmQgdG8gYmUgbG9uZ2VyIHBsYXlzLCBvdmVyYWxsPyBRdWl0ZSBwb3NzaWJseToKCgpgYGB7cn0Kc2hha0NhdCAlPiUKICBncm91cF9ieShQbGF5LGNhdGVnb3J5KSAlPiUKICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXJlb3JkZXIoUGxheSwgbikseT1uKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9Y2F0ZWdvcnkpLHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIkxlbmd0aCBvZiBTaGFrZXNwZWFyZSdzIHBsYXlzIikgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogICAgeGxhYigiUGxheSIpICsKICAgIHlsYWIoIk51bWJlciBvZiBsaW5lcyIpCmBgYAoKCgojIE4tZ3JhbXMKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXRleHQiKQpsaWJyYXJ5KHRpZHl0ZXh0KQpgYGAKClNpbmdsZSB3b3JkcyBtaWdodCBub3QgYmUgYWJsZSB0byB0ZWxsIHVzIG11Y2ggYWJvdXQgdGhlIHRleHRzLCB3aGljaCBpcyB3aHkgZXhhbWluaW5nIGNvbGxvY2F0aW9ucyBpcyBzdWNoIGEgcG9wdWxhciB0ZWNobmlxdWUuIFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuICJ0aGUga2luZyIsICJraWxsIHRoZSBraW5nIiwgYW5kICJraXNzIHRoZSBraW5nIj8gQSBsb3QsIGJ1dCB3ZSB3b24ndCBrbm93IGlmIHdlIG9ubHkgbG9vayBmb3IgaW5zdGFuY2VzIG9mICJraW5nIi4gVGhlcmUgaXMgd2hlcmUgKm4tZ3JhbXMqIGJlY29tZSB1c2VmdWwuIE4tZ3JhbXMgYXJlIHNldHMgb2YgYWRqYWNlbnQgd29yZHMsIGNhbGN1bGF0ZWQgYnkgYXNzaWduaW5nIGEgbnVtYmVyIHRvICduJy4gVGhhdCBpcywgaWYgd2Ugd2FudCBzZXRzIG9mIHR3byB3b3Jkcywgd2UgdGFsayBhYm91dCAqYmlncmFtcyouIElmIHdlIHdhbnQgc2V0cyBvZiB0aHJlZSB3b3Jkcywgd2UgdGFsayBhYm91dCAqdHJpZ3JhbXMqLiAKCiMjIFByZS1wcm9jZXNzaW5nIHRoZSBjb3JwdXMKCkZpcnN0LCB3ZSBjYW4gbG9vayBhdCB0aGUgY29ycHVzIGFzIGEgbGlzdCBvZiB3b3JkcywgcmF0aGVyIHRoYW4gYSBsaXN0IG9mIGxpbmVzIChieSBhY3QgYW5kIHNjZW5lKS4gKipXaGF0IGlzIGB1bm5lc3RfdG9rZW5zKClgIGRvaW5nIGhlcmU/KioKCmBgYHtyfQpzaGFrICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSB3b3JkKQpgYGAKCk5vdywgd2UgY2FuIGF1dG9tYXRlIHRoZSBwcm9jZXNzIG9mIGNvdW50aW5nIGhvdyBvZnRlbiBlYWNoIHdvcmQgb2NjdXJzLiBCdXQsIG9mIGNvdXJzZSwgY2VydGFpbiB3b3JkcyBhcmUgZ29pbmcgdG8gYmUgZXh0cmVtZWx5IGNvbW1vbiwgYW5kIHRob3NlIHdvcmRzIGFyZSB1bmxpa2VseSB0byBiZSBpbmZvcm1hdGl2ZS4KCmBgYHtyfQpzaGFrICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSB3b3JkKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgpPbmNlIHdlJ3ZlIGZpbHRlcmVkIG91dCBvdXIgKnN0b3Atd29yZHMqLCB0aGUgbW9zdCBmcmVxdWVudCB3b3JkcyBsb29rIHF1aXRlIGRpZmZlcmVudC4KCmBgYHtyfQpzaGFrICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSB3b3JkKSAlPiUKICBhbnRpX2pvaW4oc3RvcF93b3JkcywgYnkgPSAid29yZCIpICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKCkhvd2V2ZXIsIFNoYWtlc3BlYXJlIHVzZXMgYSBsb3Qgb2Ygd29yZHMgdGhhdCBhcmVuJ3QgaW4gb3VyIGRlZmF1bHQgc3RvcC13b3JkIGxpc3QsIHNvIHdlIGNhbiBhcHBlbmQgb3VyIG93biBjdXN0b20gbGlzdC4KCmBgYHtyfQp3b3JkIDwtIGMoTkEsInRob3UiLCJ0aGVlIiwidGh5IiwidGhpbmUiLCJkb3N0Iiwic2hhbHQiLCJ3aWx0IiwiaGFzdCIsImhhdGgiLCJzY2VuZSIsInRpcyIsImlpIiwiaWlpIiwiaXYiLCJ2IiwidmkiLCJ2aWkiKQpsZXhpY29uIDwtIHJlcCgic2hha2VzcGVhcmUiLGxlbmd0aCh3b3JkKSkKbmV3X3N0b3AgPC0gY2JpbmQod29yZCxsZXhpY29uKQpzaGFrX3N0b3AgPC0gcmJpbmQobmV3X3N0b3Asc3RvcF93b3JkcykKYGBgCgpIZXJlIGlzIGEgdmlzdWFsaXNhdGlvbiBvZiBob3cgc3RvcCB3b3JkcyBhZmZlY3QgdGhlIGNvcnB1cy4KCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuYXNwPS41LCBtZXNzYWdlPUZBTFNFfQpwMSA8LSBzaGFrICU+JQogIGFzX3RpYmJsZSguKSAlPiUKICB1bm5lc3RfdG9rZW5zKHRibD0uLCBpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IHdvcmQpICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobj44MDApICU+JQogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKHdvcmQsbikseT1uKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiTm8gc3RvcCB3b3JkcyIpCnAyIDwtIHNoYWsgJT4lCiAgYXNfdGliYmxlKC4pICU+JQogIHVubmVzdF90b2tlbnModGJsPS4sIGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gd29yZCkgJT4lCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobj44MDApICU+JQogIGdncGxvdCguLCBhZXMoeD1yZW9yZGVyKHdvcmQsbikseT1uKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgZ2d0aXRsZSgiRGVmYXVsdCBzdG9wIHdvcmRzIikKcDMgPC0gc2hhayAlPiUKICBhc190aWJibGUoLikgJT4lCiAgdW5uZXN0X3Rva2Vucyh0Ymw9LiwgaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSB3b3JkKSAlPiUKICBhbnRpX2pvaW4oc2hha19zdG9wKSAlPiUKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4+ODAwKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9cmVvcmRlcih3b3JkLG4pLHk9bikpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdndGl0bGUoIkN1c3RvbSBzdG9wIHdvcmRzIikKbXVsdGlwbG90KHAxLHAyLHAzLGNvbHM9MykKYGBgCgojIyBDb21wYXJpbmcgYWNyb3NzIHBsYXlzCgpCZWZvcmUgd2UgdHJ5IHRvIGNvbXBhcmUgYWNyb3NzIHBsYXlzLCBsZXQncyBzZWUgd2hhdCB0aGUgbW9zdCBjb21tb24gYmlncmFtcyBhcmUgb3ZlcmFsbCAoYWZ0ZXIgYmVpbmcgZmlsdGVyZWQgYnkgdGhlIGN1c3RvbSBzdG9wIHdvcmRzIGxpc3QpLgoKYGBge3J9CnNoYWsgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKQpgYGAKCiMjIyAxLWdyYW1zIChqdXN0IHJlZ3VsYXIgdG9rZW4gZnJlcXVlbmN5KQoKSWYgd2UgY2hvb3NlIGEgc3Vic2V0IG9mIHdvcmRzLCB3ZSBjYW4gbG9vayBhdCBob3cgdGhleSBhcmUgZGlzdHJpYnV0ZWQgYWNyb3NzIGRpZmZlcmVudCBwbGF5cy4gSW4gdGhpcyBjYXNlLCB3ZSBjYW4gY29tcGFyZSA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojRjg3NjZEIj5kZWF0aDwvc3Bhbj4sIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiM3Q0FFMDAiPmtpbmc8L3NwYW4+LCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojMDBCRkM0Ij5sb3ZlPC9zcGFuPiwgYW5kIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNDNzdDRkYiPnN3ZWV0PC9zcGFuPiBhY3Jvc3Mgc2l4IHBsYXlzLiBVbnN1cnByaXNpbmdseSwgKlJvbWVvIGFuZCBKdWxpZXQqIHVzZXMgdGhlIHdvcmQgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6IzAwQkZDNCI+bG92ZTwvc3Bhbj4gbW9yZSB0aGFuIGFueSBvdGhlciBwbGF5LCBhbHRob3VnaCAqTWlkc3VtbWVyIE5pZ2h0J3MgRHJlYW0qIGlzIGNsb3NlLiA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojN0NBRTAwIj5LaW5nPC9zcGFuPiBpcyBhbHNvIG11Y2ggbW9yZSBjb21tb24gaW4gcGxheXMgYWJvdXQga2luZ3MgKHN1cnByaXNlLCBzdXJwcmlzZSkuCgpgYGB7cn0Kc2hha1ssYygyLDUsNildICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHVubmVzdF90b2tlbnModGJsPS4sIGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gd29yZCkgJT4lCiAgZmlsdGVyKHdvcmQ9PSJsb3ZlIiB8IHdvcmQgPT0ia2luZyIgfCB3b3JkPT0iZGVhdGgiIHwgd29yZD09InN3ZWV0IikgJT4lCiAgZ3JvdXBfYnkoUGxheWVyLFBsYXksd29yZCkgJT4lCiAgc3VtbWFyaXNlKG49bigpKSAlPiUKICBmaWx0ZXIoICBQbGF5ID09ICJIYW1sZXQiIHwgCiAgICAgICAgICAgUGxheSA9PSAiS2luZyBMZWFyIiB8IAogICAgICAgICAgIFBsYXkgPT0gIkEgTWlkc3VtbWVyIG5pZ2h0cyBkcmVhbSIgfCAKICAgICAgICAgICBQbGF5ID09ICJPdGhlbGxvIiB8IAogICAgICAgICAgIFBsYXkgPT0gIkhlbnJ5IFYiIHwgCiAgICAgICAgICAgUGxheSA9PSAiUm9tZW8gYW5kIEp1bGlldCIpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lCiAgZ2dwbG90KC4sIGFlcyh4PXdvcmQseT1uKSkgKwogICAgZ2VvbV9iYXIoYWVzKGZpbGw9d29yZCksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBmYWNldF93cmFwKH5QbGF5KQpgYGAKCkJ1dCB0aGVyZSBhcmUgbW9yZSBpbnRlcmVzdGluZyB3b3JkcyB5b3UgY291bGQgY29tcGFyZSwgY2VydGFpbmx5LiBUaGVzZSBhcmUganVzdCBvbmUgZXhhbXBsZS4KCiMjIyBCaWdyYW1zCgpJdCdzIHByb2JhYmx5IG11Y2ggbW9yZSBpbnRlcmVzdGluZyB0byBsb29rIGF0IGNvbGxvY2F0aW9uIHRoYW4gc2ltcGxlIHdvcmQgZnJlcXVlbmN5LiBBZnRlciBhbGwsIGl0IGdpdmVzIG1vcmUgY29udGV4dC4gSG93ZXZlciwgaXQgYWxzbyByZWR1Y2VzIHRoZSBudW1iZXIgb2YgdG9rZW5zIHN1YnN0YW50aWFsbHkuCgpIZXJlLCB3ZSBjYW4gdmlzdWFsaXNlIHRocmVlIHBhaXJzIG9mIGdlbmRlcmVkIG5vdW4gcGhyYXNlczoKCk1hc2N1bGluZSB8IEZlbWluaW5lCi0tLS0tLS0gfCAtLS0tLS0tCjxzcGFuIHN0eWxlPSJjb2xvcjojRjg3NjZEIj4qKm15IGxvcmQqKjwvc3Bhbj4gICAgfCA8c3BhbiBzdHlsZT0iY29sb3I6IzAwQkZDNCI+KipteSBsYWR5Kio8L3NwYW4+CjxzcGFuIHN0eWxlPSJjb2xvcjojRjg3NjZEIj4qKm15IGZhdGhlcioqPC9zcGFuPiAgfCA8c3BhbiBzdHlsZT0iY29sb3I6IzAwQkZDNCI+KipteSBtb3RoZXIqKjwvc3Bhbj4KPHNwYW4gc3R5bGU9ImNvbG9yOiNGODc2NkQiPioqbXkgaHVzYmFuZCoqPC9zcGFuPiB8IDxzcGFuIHN0eWxlPSJjb2xvcjojMDBCRkM0Ij4qKm15IHdpZmUqKjwvc3Bhbj4KCldoYXQga2luZHMgb2YgaW5mb3JtYXRpb24gY2FuIHdlIHNlZSBpbiB0aGUgZ3JhcGg/CgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmFzcD0uNX0Kc2hhayAlPiUKICBhc190aWJibGUoKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgZmlsdGVyKGJpZ3JhbT09Im15IGxvcmQiIHwgYmlncmFtID09Im15IGxhZHkiIHwgYmlncmFtPT0ibXkgbW90aGVyIiB8IGJpZ3JhbT09Im15IGZhdGhlciIgfCBiaWdyYW09PSJteSB3aWZlIiB8IGJpZ3JhbT09Im15IGh1c2JhbmQiKSAlPiUKICBtdXRhdGUoZ2VuZGVyID0gYmlncmFtKSAlPiUKICBtdXRhdGUoZ2VuZGVyID0gcmVjb2RlX2ZhY3RvcihnZW5kZXIsCiAgICAgICAgICAgICAgICBgbXkgbG9yZGA9Im1hc2MiLAogICAgICAgICAgICAgICAgYG15IGZhdGhlcmA9Im1hc2MiLAogICAgICAgICAgICAgICAgYG15IGh1c2JhbmRgPSJtYXNjIiwKICAgICAgICAgICAgICAgIGBteSBsYWR5YD0iZmVtIiwKICAgICAgICAgICAgICAgIGBteSBtb3RoZXJgPSJmZW0iLAogICAgICAgICAgICAgICAgYG15IHdpZmVgPSJmZW0iKSkgJT4lCiAgZ3JvdXBfYnkoUGxheWVyLFBsYXksYmlncmFtLGdlbmRlcikgJT4lCiAgc3VtbWFyaXNlKG49bigpKSAlPiUKICBtdXRhdGUoYmlncmFtRmFjID0gZmFjdG9yKGJpZ3JhbSwgbGV2ZWxzPWMoIm15IGxvcmQiLCAibXkgaHVzYmFuZCIsICJteSBmYXRoZXIiLCAibXkgbGFkeSIsICJteSB3aWZlIiwgIm15IG1vdGhlciIpKSkgJT4lCiAgIyB0b28gYm9yaW5nCiAgZmlsdGVyKCAgUGxheSAhPSAiSGVucnkgVkkgUGFydCAxIiAmCiAgICAgICAgICAgUGxheSAhPSAiSGVucnkgVkkgUGFydCAyIiAmCiAgICAgICAgICAgUGxheSAhPSAiSGVucnkgVkkgUGFydCAzIiAmCiAgICAgICAgICAgUGxheSAhPSAiUGVyaWNsZXMiICYgCiAgICAgICAgICAgUGxheSAhPSAiVGltb24gb2YgQXRoZW5zIiAmIAogICAgICAgICAgIFBsYXkgIT0gIlRoZSBUZW1wZXN0IikgJT4lCiAgIyB0b28gc2tld2VkCiAgZmlsdGVyKCAgUGxheSAhPSAiSGFtbGV0IiAmCiAgICAgICAgICAgUGxheSAhPSAiVHJvaWx1cyBhbmQgQ3Jlc3NpZGEiICYKICAgICAgICAgICBQbGF5ICE9ICJSaWNoYXJkIElJSSIgJgogICAgICAgICAgIFBsYXkgIT0gIlRpdHVzIEFuZHJvbmljdXMiICYgCiAgICAgICAgICAgUGxheSAhPSAiSGVucnkgVklJSSIgJiAKICAgICAgICAgICBQbGF5ICE9ICJNdWNoIEFkbyBhYm91dCBub3RoaW5nIikgJT4lCiAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHg9YmlncmFtRmFjLHk9bikpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsPWdlbmRlciksc3RhdD0iaWRlbnRpdHkiKSArCiAgICBzY2FsZV95X2xvZzEwKCkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGZhY2V0X3dyYXAoflBsYXksbnJvdz0zKQpgYGAKCgojIyBOZXR3b3JrcwoKCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImlncmFwaCIpCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3JhcGgiKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmxpYnJhcnkoZ3JpZCkKCmEgPC0gZ3JpZDo6YXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZT0yMi41LCBsZW5ndGggPSB1bml0KC4xLCAiaW5jaGVzIikpCmBgYAoKSSB0aGluayB0aGUgbW9zdCBleGNpdGluZyB3YXkgdG8gdXNlIG4tZ3JhbXMgaXMgcHJvYmFibHkgbmV0d29yayBncmFwaHMuIFRoZXNlIGdyYXBocyBzaG93IHdoYXQgd29yZHMgY28tb2NjdXIsIGFuZCBpbiB3aGF0IG9yZGVyLiBNb3Jlb3ZlciwgdGhleSBjYW4gZW5jb2RlIGEgbnVtYmVyIG9mIGRpbWVuc2lvbnMgdmlzdWFsbHksIHdoaWNoIHdvdWxkIGJlIHZlcnkgZGlmZmljdWx0IHRvIGNhbGN1bGF0ZSBieSBoYW5kIG9yIHBsb3QgaW4gYSBtb3JlIHN0YW5kYXJkIHF1YW50aXRhdGl2ZSBtZXRob2QuCgojIyMgQmlncmFtcwoKRnJvbSB0aGUgbGlzdCBvZiBiaWdyYW1zIHdlIGNhbiBnZW5lcmF0ZSBmcm9tIG91ciBjb3JwdXMsIHdlIGNhbiBwbG90IGEgbmV0d29yayBncmFwaCBpbiB3aGljaCB0aGUgc2hhZGUgb2YgdGhlIGNvbm5lY3Rpb24gYmV0d2VlbiBub2RlcyBpbmRpY2F0ZXMgdGhlIGZyZXF1ZW5jeSBvZiB0aGUgYmlncmFtIChkYXJrZXIgbWVhbnMgbW9yZSBmcmVxdWVudCkuIE1vcmVvdmVyLCB0aGVzZSBjb25uZWN0aW9ucyAoImVkZ2VzIikgYXJlIGRpcmVjdGlvbmFsLCBzbyB3ZSBjYW4gc2VlIHdoaWNoIG9yZGVyIHRoZSB3b3JkcyBhcmUgb2NjdXJpbmcgaW4uCgpgYGB7cn0Kc2hhayAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMjIpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya2JsdWUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWw9VFJVRSkgKyAKICAgIHRoZW1lX3ZvaWQoKQpgYGAKCldlIGNhbiBhbHNvIHVzZSB0aGVzZSBwbG90cyB0byBjb21wYXJlIGFjcm9zcyBwbGF5cyAoYWx0aG91Z2ggdG9rZW4gZnJlcXVlbmN5IGJlZ2lucyB0byBkcm9wIHByZWNpcGl0b3VzbHkpLiBIZXJlLCB3ZSBjYW4gc2VlIHRoYXQgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6c2FsbW9uIj5Ud2VsZnRoIE5pZ2h0PC9zcGFuPiBoYXMgbm90YWJsZSBjb25uZWN0aW9ucyBiZXR3ZWVuIGl0ZW1zIG9mIGNsb3RoaW5nICh5ZWxsb3cgc3RvY2tpbmdzLCBjcm9zcyBnYXJ0ZXJlZCksIHdoZXJlYXMgPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6bGlnaHRibHVlIj5IYW1sZXQ8L3NwYW4+IGFuZCA8c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjpncmVlbjIiPlJvbWVvIGFuZCBKdWxpZXQ8L3NwYW4+IGhhdmUgbW9yZSB0b2tlbnMgcmVmZXJyaW5nIHRvIHBlb3BsZSBhbmQgdGhlaXIgc3RhZ2UgZGlyZWN0aW9ucy4gPHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6bGlnaHRibHVlIj5IYW1sZXQ8L3NwYW4+IGFkZGl0aW9uYWxseSBoYXMgYSBub3RhYmxlIG51bWJlciBvZiAiZmF0aGVyJ3MgZGVhdGgiIGJpZ3JhbXMsIHdoaWxlIDxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOmdyZWVuMiI+Um9tZW8gYW5kIEp1bGlldDwvc3Bhbj4gbWVudGlvbnMgImNvdW50eSBQYXJpcyIgZnJlcXVlbnRseS4KCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuYXNwPS4zfQpwMSA8LSBzaGFrICU+JQogIGZpbHRlcihQbGF5PT0iSGFtbGV0IikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDYpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya2JsdWUiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWw9VFJVRSkgKyAKICAgIHRoZW1lX3ZvaWQoKQoKcDIgPC0gc2hhayAlPiUKICBmaWx0ZXIoUGxheSA9PSAiVHdlbGZ0aCBOaWdodCIpICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiA2KSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgZWRnZV9jb2xvdXI9ImRhcmtyZWQiLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGFycm93ID0gYSwgZW5kX2NhcCA9IGNpcmNsZSguMDcsICdpbmNoZXMnKSkgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gInNhbG1vbiIsIHNpemUgPSA1KSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWw9VFJVRSkgKyAKICAgIHRoZW1lX3ZvaWQoKQoKcDMgPC0gc2hhayAlPiUKICBmaWx0ZXIoUGxheSA9PSAiUm9tZW8gYW5kIEp1bGlldCIpICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiA2KSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgZWRnZV9jb2xvdXI9ImRhcmtncmVlbiIsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAiZ3JlZW4yIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpCgptdWx0aXBsb3QocDEscDIscDMsY29scz0zKQpgYGAKCklmIHdlIGV4Y2x1ZGUgc3RhZ2UgZGlyZWN0aW9ucyBhbmQgY29tcGFyZSBhY3Jvc3Mgc2l4IHBsYXlzLCB3ZSBzdGFydCB0byBzZWUgZGlzdGluY3QgdGhlbWVzIGFwcGVhci4gCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmFzcD0uNX0KcDEgPC0gc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBmaWx0ZXIoUGxheT09IkhhbWxldCIpICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSBiaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAyKSAlPiUKICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkgJT4lCiAgZmlsdGVyKG4gPiAzKSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgZWRnZV9jb2xvdXI9ImRhcmtibHVlIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJsaWdodGJsdWUiLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkgKwogICAgZ2d0aXRsZSgiSGFtbGV0IikKCnAyIDwtIHNoYWsgJT4lCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIlR3ZWxmdGggTmlnaHQiKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMykgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJkYXJrcmVkIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJzYWxtb24iLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkgKwogICAgZ2d0aXRsZSgiVHdlbGZ0aCBOaWdodCIpCgpwMyA8LSBzaGFrICU+JQogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIGZpbHRlcihQbGF5ID09ICJSb21lbyBhbmQgSnVsaWV0IikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDMpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya2dyZWVuIiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJncmVlbjIiLCBzaXplID0gNSkgKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsPVRSVUUpICsgCiAgICB0aGVtZV92b2lkKCkgKwogICAgZ2d0aXRsZSgiUm9tZW8gYW5kIEp1bGlldCIpCgpwNCA8LSBzaGFrICU+JQogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIGZpbHRlcihQbGF5ID09ICJPdGhlbGxvIikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDMpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya29yYW5nZSIsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAib3JhbmdlIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpICsKICAgIGdndGl0bGUoIk90aGVsbG8iKQoKcDUgPC0gc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBmaWx0ZXIoUGxheSA9PSAiSGVucnkgSVYiKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gYmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMykgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJjYWRldGJsdWU0Iiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJjeWFuIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpICsKICAgIGdndGl0bGUoIkhlbnJ5IElWIikKCnA2IDwtIHNoYWsgJT4lCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIlRoZSBUZW1wZXN0IikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IGJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUKICBmaWx0ZXIobiA+IDMpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0idmlvbGV0Iiwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICAgIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJtYWdlbnRhIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpICsKICAgIGdndGl0bGUoIlRoZSBUZW1wZXN0IikKCm11bHRpcGxvdChwMSxwMixwMyxwNCxwNSxwNixjb2xzPTMpCmBgYAoKQWxsIG9mIHRoZXNlIHdvcmRzIGFyZSBmb3VuZCBkaXJlY3RseSBhZGphY2VudC4gV2hhdCBhYm91dCB3aGVuIHdvcmRzIGFyZSBzbGlnaHRpbmcgZnVydGhlciBhcGFydD8gVGhhdCBpcywgbGFuZ3VhZ2UgaXMgbm90IGxpbmVhci4gSG93IGNhbiB3ZSB2aXN1YWxpc2UgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRvcGljcyB0aGF0IGFyZSBzb21ld2hhdCBsZXNzIGltbWVkaWF0ZT8KCiMjIyBUcmlncmFtcwoKT25lIG9mIHRoZSBzaW1wbGVzdCB3YXlzIHRvIHZpc3VhbGlzZSBtb3JlIGRpc3RhbnQgY29ubmVjdGlvbnMgaXMgdGhlIGluY2x1ZGUgbW9yZSB3b3JkcyBpbiBlYWNoIG5ldHdvcmsuIEhlcmUgaXMgYSBncmFwaCBvZiB0cmlncmFtcyBmb3IgdGhlIGVudGlyZSBjb3JwdXMsIGV4Y2x1ZGluZyBzdGFnZSBkaXJlY3Rpb25zLiAoQWN0dWFsbHksIEkgdGhpbmsgdGhpcyBncmFwaCBtYXkgYmUgb2Ygb25seSB0aGUgZmlyc3QgdHdvIGVsZW1lbnRzIG9mIGVhY2ggdHJpZ3JhbSwgYnV0IEkgd2lsbCBoYXZlIHRvIHNvcnQgdGhhdCBvdXQgbGF0ZXIuKQoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5hc3A9LjV9CnNoYWsgJT4lCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgdW5uZXN0X3Rva2VucyhpbnB1dCA9IFBsYXllckxpbmUsIG91dHB1dCA9IHRyaWdyYW0sIHRva2VuID0gIm5ncmFtcyIsIG4gPSAzKSAlPiUKICBzZXBhcmF0ZSh0cmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIsICJ3b3JkMyIpLCBzZXAgPSAiICIpICU+JSAjIHNlcGFyYXRlcyBiaWdyYW0gaW50byB0d28gY29sdW1ucywgb25lIGZvciBlYWNoIHdvcmQKICBmaWx0ZXIoIXdvcmQxICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIGZpcnN0IGNvbHVtbgogIGZpbHRlcighd29yZDIgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gc2Vjb25kIGNvbHVtbgogIGZpbHRlcighd29yZDMgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gdGhpcmQgY29sdW1uCiAgY291bnQod29yZDEsIHdvcmQyLCB3b3JkMywgc29ydCA9IFRSVUUpICU+JQogIGZpbHRlcihuID4gMikgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICAgIGdlb21fZWRnZV9saW5rKGFlcyhlZGdlX2FscGhhID0gbiksIGVkZ2VfY29sb3VyPSJkYXJrYmx1ZSIsIHNob3cubGVnZW5kID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDUpICsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IG5hbWUpLCByZXBlbD1UUlVFKSArIAogICAgdGhlbWVfdm9pZCgpCmBgYAoKCldoYXQgaGFwcGVucyBpZiB3ZSB0cmVhdCB0aGUgZmlyc3QgcGFpciBhbmQgc2Vjb25kIHBhaXIgb2YgdHJpZ3JhbXMgYXMgc2VwYXJhdGUgYmlncmFtcyBhbmQgZ3JhcGggdGhlbSBhcyBiZWZvcmU/CgpXZSBzaG91bGQgYmUgYWJsZSB0byB2aXN1YWxpc2UgbG9uZ2VyIGRpc3RhbmNlIHJlbGF0aW9ucyBvZiB0aGVzZSBjb2xsb2NhdGlvbnMuIFRoaXMgaXMgb25seSBhIHRhc3RlIG9mIHdoYXQga2luZHMgb2YgbmV0d29yayBncmFwaHMgY2FuIGJlIGdlbmVyYXRlZCwgc2luY2UgdGhlIHR5cGUgb2YgZ3JhcGggYW5kIGNvbnRlbnQgd2lsbCB2YXJ5IHRyZW1lbmRvdXNseSBiYXNlZCBvbiB5b3VyIHVuaXF1ZSByZXNlYXJjaCBxdWVzdGlvbnMuCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmFzcD0uNzV9CncxdzIgPC0gc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICB1bm5lc3RfdG9rZW5zKGlucHV0ID0gUGxheWVyTGluZSwgb3V0cHV0ID0gdHJpZ3JhbSwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDMpICU+JQogIHNlcGFyYXRlKHRyaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiwgIndvcmQzIiksIHNlcCA9ICIgIikgJT4lICMgc2VwYXJhdGVzIGJpZ3JhbSBpbnRvIHR3byBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggd29yZAogIGZpbHRlcighd29yZDEgJWluJSBzaGFrX3N0b3Akd29yZCkgJT4lICMgZmlsdGVycyBzdG9wIHdvcmRzIGZyb20gZmlyc3QgY29sdW1uCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBzZWNvbmQgY29sdW1uCiAgZmlsdGVyKCF3b3JkMyAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSB0aGlyZCBjb2x1bW4KICBjb3VudCh3b3JkMSwgd29yZDIsIHdvcmQzLCBzb3J0ID0gVFJVRSkgJT4lCiAgbXV0YXRlKHNldCA9IDEpICU+JQogIHRyYW5zbXV0ZSh3b3JkMT13b3JkMSx3b3JkMj13b3JkMixuPW4sc2V0PXNldCkKdzJ3MyA8LSBzaGFrICU+JQogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIHVubmVzdF90b2tlbnMoaW5wdXQgPSBQbGF5ZXJMaW5lLCBvdXRwdXQgPSB0cmlncmFtLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgJT4lCiAgc2VwYXJhdGUodHJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiLCAid29yZDMiKSwgc2VwID0gIiAiKSAlPiUgIyBzZXBhcmF0ZXMgYmlncmFtIGludG8gdHdvIGNvbHVtbnMsIG9uZSBmb3IgZWFjaCB3b3JkCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHNoYWtfc3RvcCR3b3JkKSAlPiUgIyBmaWx0ZXJzIHN0b3Agd29yZHMgZnJvbSBmaXJzdCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQyICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHNlY29uZCBjb2x1bW4KICBmaWx0ZXIoIXdvcmQzICVpbiUgc2hha19zdG9wJHdvcmQpICU+JSAjIGZpbHRlcnMgc3RvcCB3b3JkcyBmcm9tIHRoaXJkIGNvbHVtbgogIGNvdW50KHdvcmQxLCB3b3JkMiwgd29yZDMsIHNvcnQgPSBUUlVFKSAlPiUKICBtdXRhdGUoc2V0ID0gMikgJT4lCiAgdHJhbnNtdXRlKHdvcmQxPXdvcmQyLHdvcmQyPXdvcmQzLG49bixzZXQ9c2V0KQp3WHdZIDwtIGJpbmRfcm93cyh3MXcyLHcydzMpCgp3WHdZICU+JQogIGZpbHRlcihuPj0zKSAlPiUKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKSAlPiUKICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKwogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArCiAgICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4pLCBlZGdlX2NvbG91cj0iZGFya2JsdWUiLCBzaG93LmxlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArCiAgICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgYWxwaGE9Ljc1LCByZXBlbD1UUlVFKSArICMgLCB2anVzdCA9IDEsIGhqdXN0ID0gMSkgKwogICAgdGhlbWVfdm9pZCgpCmBgYAoKIyBIZWF0bWFwcwoKQW5vdGhlciB2aXN1YWxpc2F0aW9uIHRvb2wgdGhhdCBjYW4gYmUgdmVyeSBoZWxwZnVsIGZvciBzaG93aW5nIGRpc3RyaWJ1dGlvbnMgb2YgZXZlbnRzIGFjcm9zcyBhIHN0cnVjdHVyZSBpcyB0aGUgaGVhdG1hcC4gQSBoZWF0bWFwIGlzIGEgY29tcGxleCBoaXN0b2dyYW0sIHdoaWNoIGFsbG93cyBtdWx0aXBsZSBzdWJzZXRzIG9mIHRoZSBkYXRhIHRvIGJlIGNvbXBhcmVkIHNpZGUtdG8tc2lkZS4KCgpJbiB0aGlzIGV4YW1wbGUsIHdlIGNhbiBsb29rIGF0IHRoZSB2YXJpYWJpbGl0eSBvZiBsZW5ndGggb2Ygc3Vic2VjdGlvbnMgKGFjdHMgYW5kIHNjZW5lcykgYXMgZGV0ZXJtaW5lZCBieSBudW1iZXIgb2Ygd29yZHMuIEZpcnN0LCBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgbnVtYmVycy4gU2luY2UgdGhlcmUgYXJlIHNvIG1hbnkgYWN0cyBhbmQgc2NlbmVzIGFjcm9zcyBhbGwgdGhlIHBsYXlzLCBpdCBpcyB1bndlaWxkeSB0byBsb29rIGF0IHN1Y2ggYSB0YWJsZS4KCmBgYHtyfQpzaGFrICU+JQogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIG11dGF0ZShBY3RTY2VuZUxpbmUyID0gQWN0U2NlbmVMaW5lKSAlPiUKICBzZXBhcmF0ZShBY3RTY2VuZUxpbmUyLCBjKCJhY3QiLCAic2NlbmUiLCAibGluZSIpKSAlPiUKICBjb3VudChQbGF5LGFjdCxzY2VuZSwgc29ydD1UUlVFKSAlPiUKICB0cmFuc211dGUocGxheT1QbGF5LCBhY3Q9YXMubnVtZXJpYyhhY3QpLCBzY2VuZT1hcy5udW1lcmljKHNjZW5lKSwgbj1uKSAlPiUKICBncm91cF9ieShwbGF5KQpgYGAKCldlIGNvdWxkIGxvb2sgYXQgYSBzZXJpZXMgb2YgaGlzdG9ncmFtcyB0byBzZWUgaG93IHBsYXlzIHZhcnkgaW4gdGhlaXIgZGlzdHJpYnV0aW9u4oCmCgpgYGB7cn0Kc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBtdXRhdGUoQWN0U2NlbmVMaW5lMiA9IEFjdFNjZW5lTGluZSkgJT4lCiAgc2VwYXJhdGUoQWN0U2NlbmVMaW5lMiwgYygiYWN0IiwgInNjZW5lIiwgImxpbmUiKSkgJT4lCiAgY291bnQoUGxheSxhY3QsIHNvcnQ9VFJVRSkgJT4lCiAgdHJhbnNtdXRlKHBsYXk9UGxheSwgYWN0PWFzLmludGVnZXIoYWN0KSwgbj1uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YWN0KSkgKyAKICAgIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gbiksIHN0YXQ9ImlkZW50aXR5IikgKyBmYWNldF93cmFwKH5wbGF5KQpgYGAKCgpIb3dldmVyLCBpdCBpcyBtdWNoIGVhc2llciBhbmQgcXVpY2tlciB0byBleHRyYWN0IHRoaXMgaW5mb3JtYXRpb24gZnJvbSBhIGhlYXRtYXAgKGFsdGhvdWdoIHRoZSBwcmVjaXNlIG51bWJlcnMgYXJlIGxvc3QgaW4gdGhpcyB2ZXJzaW9uKS4KCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuYXNwPS43NX0Kc2hhayAlPiUKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBtdXRhdGUoQWN0U2NlbmVMaW5lMiA9IEFjdFNjZW5lTGluZSkgJT4lCiAgc2VwYXJhdGUoQWN0U2NlbmVMaW5lMiwgYygiYWN0IiwgInNjZW5lIiwgImxpbmUiKSkgJT4lCiAgY291bnQoUGxheSxhY3QsIHNvcnQ9VFJVRSkgJT4lCiAgdHJhbnNtdXRlKHBsYXk9UGxheSwgYWN0PWFzLmludGVnZXIoYWN0KSwgbj1uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YWN0LHk9cmVvcmRlcihwbGF5LCBuKSkpICsgCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBuKSwgY29sb3VyID0gIndoaXRlIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAic3RlZWxibHVlIikKYGBgCgpMZXQncyBmb2N1cyBpbiBvbiBhIHN1YnNldCBvZiBwbGF5cyBpbiBvcmRlciB0byBzaW1wbGlmeSB0aGUgdmlzdWFsaXNhdGlvbi4gRnJvbSBhIHNpbXBsZSB2aXN1YWwgaW5zcGVjdGlvbiwgaXQgYXBwZWFycyB0aGF0ICoqQWN0IFYqKiB0ZW5kcyB0byBiZSB0aGUgbGlnaHRlc3Qgb24gd29yZHMsIHdpdGggdGhlIHZlcnkgbm90YWJsZSBleGNlcHRpb24gb2YgKkxvdmUncyBMYWJvdXIncyBMb3N0Ki4gT3RoZXJ3aXNlLCAqKkFjdCBJKiogaXMgZ2VuZXJhbGx5IGZhaXJseSBoZWF2eSBvbiB3b3JkcywgYW5kICoqQWN0IElJKiogdGVuZHMgdG8gYmUgYSBiaXQgbGlnaHRlci4gQXJtZWQgd2l0aCB0aGF0IChzdXBlcmZpY2lhbCkgb2JzZXJ2YXRpb24sIHdlIG1pZ2h0IGJlIGFibGUgdG8gY2hlY2sgd2hldGhlciBvdXIgZXllcyBkZWNlaXZlIHVzIG9yIHdoZXRoZXIgdGhlcmUncyBzb21ldGhpbmcgdG8gaXQuIChCdXQgSSdsbCBsZWF2ZSB0aGF0IGV4cGxvcmF0aW9uIGZvciBhbm90aGVyIHRpbWUuKQoKYGBge3J9CnNoYWsgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIkhhbWxldCIgfCBQbGF5ID09ICJLaW5nIEpvaG4iIHwgUGxheSA9PSAiVGhlIFRlbXBlc3QiIHwgCiAgICAgICAgICAgUGxheSA9PSAiQ3ltYmVsaW5lIiB8IFBsYXkgPT0gIk1lYXN1cmUgZm9yIG1lYXN1cmUiIHwgUGxheSA9PSAiVGltb24gb2YgQXRoZW5zIiB8IAogICAgICAgICAgIFBsYXkgPT0gIlJpY2hhcmQgSUlJIiB8IFBsYXkgPT0gIkxvdmVzIExhYm91cnMgTG9zdCIgfCBQbGF5ID09ICJBIFdpbnRlcnMgVGFsZSIgfCAKICAgICAgICAgICBQbGF5ID09ICJPdGhlbGxvIiB8IFBsYXkgPT0gIlJvbWVvIGFuZCBKdWxpZXQiIHwgUGxheSA9PSAiSGVucnkgViIpICU+JSAKICBmaWx0ZXIoQWN0U2NlbmVMaW5lICE9ICIiKSAlPiUKICBtdXRhdGUoQWN0U2NlbmVMaW5lMiA9IEFjdFNjZW5lTGluZSkgJT4lCiAgc2VwYXJhdGUoQWN0U2NlbmVMaW5lMiwgYygiYWN0IiwgInNjZW5lIiwgImxpbmUiKSkgJT4lCiAgY291bnQoUGxheSxhY3QsIHNvcnQ9VFJVRSkgJT4lCiAgdHJhbnNtdXRlKHBsYXk9UGxheSwgYWN0PWFzLmludGVnZXIoYWN0KSwgbj1uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YWN0LHk9cmVvcmRlcihwbGF5LCBuKSkpICsgCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBuKSwgY29sb3VyID0gIndoaXRlIikgKyBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAic3RlZWxibHVlIikKYGBgCgpUaGUgbnVtYmVyIG9mIHNjZW5lcyBwZXIgYWN0IHRlbmRzIGZvciB2YXJ5LCBhcyBkb2VzIHRoZSBudW1iZXIgb2Ygd29yZHMgcGVyIHNjZW5lLiBNYXliZSB0aGVyZSBpcyBzb21lIHBhdHRlcm4gd2UgY2FuIGRldGVjdCBieSBwbG90dGluZyB0aGUgbnVtYmVyIG9mIHdvcmRzIGJ5IHNjZW5lLCBieSBhY3QsIGFuZCBieSBwbGF5LiBUaGlzIGZpZ3VyZSBpcyBhYmxlIHRvIGNvbW11bmljYXRlIGEgdHJlbWVuZG91cyBhbW91bnQgb2YgaW5mb3JtYXRpb24gaW4gYSB2ZXJ5IHNtYWxsIHNwYWNlLiBPZiBjb3Vyc2UsIG9uZSBtdXN0IHN0aWxsIGJlIHdhbGtlZCB0aHJvdWdoIGl0IHRvIGdldCB0aGUgZnVsbCBnaXN0IG9mIHdoYXQgaXMgc2hvd24uIFRoZSB0d28gd29yZGllc3Qgc2NlbmVzIGluIHRoaXMgc3Vic2V0IG9mIHRoZSBjb3JwdXMgYXJlIGluICpMb3ZlJ3MgTGFib3VyJ3MgTG9zdCogKipBY3QgViwgU2NlbmUgSUkqKiAodGhlIHZlcnkgbGFzdCBzY2VuZSBpbiB0aGUgcGxheSkgYW5kICpBIFdpbnRlcidzIFRhbGUqICoqQWN0IElWLCBTY2VuZSBJVioqLiBJIGRvbid0IGtub3cgd2hhdCBoYXBwZW5zIGluIHRoZXNlIHR3byBzY2VuZXMsIGJ1dCBtYXliZSBzb21lb25lIHdobyBpcyBmYW1pbGlhciB3aXRoIHRoZSBjb250ZW50IGNvdWxkIGlkZW50aWZ5IHdoYXQgbWFrZXMgdGhlc2UgdHdvIHNjZW5lcyBzdGFuZCBvdXQuCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmFzcD0uMzV9CnNoYWsgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIkhhbWxldCIgfCBQbGF5ID09ICJLaW5nIEpvaG4iIHwgUGxheSA9PSAiVGhlIFRlbXBlc3QiIHwgCiAgICAgICAgICAgUGxheSA9PSAiQ3ltYmVsaW5lIiB8IFBsYXkgPT0gIk1lYXN1cmUgZm9yIG1lYXN1cmUiIHwgUGxheSA9PSAiVGltb24gb2YgQXRoZW5zIiB8IAogICAgICAgICAgIFBsYXkgPT0gIlJpY2hhcmQgSUlJIiB8IFBsYXkgPT0gIkxvdmVzIExhYm91cnMgTG9zdCIgfCBQbGF5ID09ICJBIFdpbnRlcnMgVGFsZSIgfCAKICAgICAgICAgICBQbGF5ID09ICJPdGhlbGxvIiB8IFBsYXkgPT0gIlJvbWVvIGFuZCBKdWxpZXQiKSAlPiUgCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgbXV0YXRlKEFjdFNjZW5lTGluZTIgPSBBY3RTY2VuZUxpbmUpICU+JQogIHNlcGFyYXRlKEFjdFNjZW5lTGluZTIsIGMoImFjdCIsICJzY2VuZSIsICJsaW5lIikpICU+JQogIGNvdW50KFBsYXksYWN0LHNjZW5lLCBzb3J0PVRSVUUpICU+JQogIHRyYW5zbXV0ZShwbGF5PVBsYXksIGFjdD1hcy5pbnRlZ2VyKGFjdCksIHNjZW5lPWFzLmludGVnZXIoc2NlbmUpLCBuPW4pICU+JQogIGdncGxvdChhZXMoeD1zY2VuZSx5PXBsYXkpKSArIAogICAgZ2VvbV90aWxlKGFlcyhmaWxsID0gbiksIGNvbG91ciA9ICJ3aGl0ZSIpICsgCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAicmVkMiIpICsKICAgIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygwOjgpKSArCiAgICB0aGVtZV9kYXJrKCkgKyAKICAgIGZhY2V0X3dyYXAofmFjdCwgbmNvbCA9IDUpCmBgYAoKQSBzaW1pbGFyIHN0eWxlIGZpZ3VyZSBjb3VsZCBhbHNvIGlsbHVzdHJhdGUgd2hlcmUgZWFjaCBjaGFyYWN0ZXIgaGFzIHRoZSBidWxrIG9mIHRoZWlyIGxpbmVzLiBIZXJlLCB3ZSBjYW4gc2VlIEhhbWxldCBpcyBwb3NpdGl2ZWx5IHZlcmJvc2UgaW4gKipTY2VuZSBJSSoqIGluICoqQWN0cyBJSSwgSUlJLCBWKiosIGJ1dCBvdGhlcndpc2Ugb25seSBtYXJnaW5hbGx5IHdvcmRpZXIgdGhhbiB0aGUgb3RoZXIgcGxheWVycy4gTW9yZW92ZXIsIHRoZXJlIHNlZW1zIHRvIGJlIHRoZSBtb3N0IGV2ZW4gZGlzdHJpYnV0aW9uIG9mIGxpbmVzIChuZWl0aGVyIHZlcnkgZGFyayBub3IgZW50aXJlbHkgcGFsZSwgYXMgY29sb3VyLWNvZGVkKSBpbiAqKkFjdCBJKiosIHdoZXJlYXMgdGhlIG90aGVyIGFjdHMgYXJlIHF1aXRlIHNrZXdlZCB0b3dhcmQgdGhlIGZldyBtYWluIHBsYXllcnMuCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmFzcD0uNX0Kc2hhayAlPiUKICBmaWx0ZXIoUGxheSA9PSAiSGFtbGV0IikgJT4lIAogIGZpbHRlcihBY3RTY2VuZUxpbmUgIT0gIiIpICU+JQogIG11dGF0ZShBY3RTY2VuZUxpbmUyID0gQWN0U2NlbmVMaW5lKSAlPiUKICBzZXBhcmF0ZShBY3RTY2VuZUxpbmUyLCBjKCJhY3QiLCAic2NlbmUiLCAibGluZSIpKSAlPiUKICBjb3VudChQbGF5ZXIsYWN0LHNjZW5lLCBzb3J0PVRSVUUpICU+JQogIHRyYW5zbXV0ZShwbGF5ZXI9UGxheWVyLCBhY3Q9YXMuaW50ZWdlcihhY3QpLCBzY2VuZT1hcy5pbnRlZ2VyKHNjZW5lKSwgbj1uKSAlPiUKICBnZ3Bsb3QoYWVzKHg9c2NlbmUseT1yZW9yZGVyKHBsYXllcixuKSkpICsgCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBuKSwgY29sb3VyID0gIndoaXRlIikgKyAKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJyZWQyIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDA6OCkpICsKICAgIHRoZW1lX2RhcmsoKSArIAogICAgZmFjZXRfd3JhcCh+YWN0LCBuY29sID0gNSkKYGBgCgpUaGUgc2FtZSB0eXBlIG9mIHBsb3QgY2FuIGJlIHVzZWQgdG8gZXhhbWluZSAqVHdlbGZ0aCBOaWdodCosIHdoaWNoIGlzIHNob3J0ZXIsIG1vcmUgZXZlbmx5IGRpc3RyaWJ1dGVkLCBhbmQgbXVjaCBsZXNzIHNvbGlsb3F1eS1kcml2ZW4uIAoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5hc3A9LjV9CnNoYWsgJT4lCiAgZmlsdGVyKFBsYXkgPT0gIlR3ZWxmdGggTmlnaHQiKSAlPiUgCiAgZmlsdGVyKEFjdFNjZW5lTGluZSAhPSAiIikgJT4lCiAgbXV0YXRlKEFjdFNjZW5lTGluZTIgPSBBY3RTY2VuZUxpbmUpICU+JQogIHNlcGFyYXRlKEFjdFNjZW5lTGluZTIsIGMoImFjdCIsICJzY2VuZSIsICJsaW5lIikpICU+JQogIGNvdW50KFBsYXllcixhY3Qsc2NlbmUsIHNvcnQ9VFJVRSkgJT4lCiAgdHJhbnNtdXRlKHBsYXllcj1QbGF5ZXIsIGFjdD1hcy5pbnRlZ2VyKGFjdCksIHNjZW5lPWFzLmludGVnZXIoc2NlbmUpLCBuPW4pICU+JQogIGdncGxvdChhZXMoeD1zY2VuZSx5PXJlb3JkZXIocGxheWVyLG4pKSkgKyAKICAgIGdlb21fdGlsZShhZXMoZmlsbCA9IG4pLCBjb2xvdXIgPSAid2hpdGUiKSArIAogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gInJlZDIiKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMDo4KSkgKwogICAgdGhlbWVfZGFyaygpICsgCiAgICBmYWNldF93cmFwKH5hY3QsIG5jb2wgPSA1KQpgYGAKCiMgV3JhcCB1cAoKV2hhdCB0aGlzIGFsbCBzZWVtcyB0byB0ZWxsIHVzIGlzIHRoYXQgd2UgY2FuIHZpc3VhbGlzZSB0aGUgc3RydWN0dXJlIG9mIHRoZSBwbGF5LCBzZXBhcmF0ZSBmcm9tIHRoZWlyIGNvbnRlbnQuIElzIHRoaXMgdXNlZnVsIHRvIHlvdT8gSXQgd2lsbCBkZXBlbmQgb24gd2hhdCBraW5kcyBvZiByZXNlYXJjaCBxdWVzdGlvbnMgeW91IGFyZSBpbnRlcmVzdGVkIGluLiBIb3dldmVyLCBpZiBhbnkgb2YgeW91ciBxdWVzdGlvbnMgaW52b2x2ZSBjb3VudGluZyB0aGluZ3MgKHdvcmRzLCBsaW5lcywgYXBwZWFyYW5jZXMsIGNvbGxvY2F0aW9ucywgbmdyYW1zLCBldGMpLCB0aGVuIGl0IGlzIHBvc3NpYmxlIGFuZCBldmVuIGxpa2VseSB0aGF0IHZpc3VhbGlzYXRpb25zIGNhbiBoZWxwIGNvbW11bmljYXRlIHRoYXQgaW5mb3JtYXRpb24gaW4gYSBzdWNjaW5jdCBhbmQgZWFzaWx5IHJlYWQgd2F5LgoKT2YgY291cnNlLCBjb3VudGluZyB0aGluZ3MgaXMgb25seSBnb2luZyB0byBiZSBoZWxwZnVsIGZvciBzb21lIHBvc3NpYmxlIHF1ZXN0aW9ucy4gQnV0LCBpdCdzIGltcG9ydGFudCB0byBrbm93IHRoYXQgbm90IGV2ZXJ5dGhpbmcgdGhhdCBjYW4gYmUgY291bnRlZCBpcyBhIHNpbXBsZSBudW1iZXIuIFRoZXJlIGFyZSByZWxhdGl2ZSBmcmVxdWVuY2llcywgY29sbG9jYXRpb25zIGFuZCBuLWdyYW1zLCBhbmQgY2hhbmdlcyBvdmVyICJ0aW1lIiAoaS5lLiwgaW4gZGlmZmVyZW50IGFjdHMsIHNjZW5lcywgY2hhcHRlcnMsIGJ5IHB1YmxpY2F0aW9uIHllYXIsIGV0YykuIFdlIGNhbiB1c2UgdGhlc2UgcmVsYXRpb25zIGJldHdlZW4gbnVtYmVycyB0byBjcmVhdGUgZWxlZ2FudCBmaWd1cmVzIHRoYXQgbWlnaHQgb3IgbWlnaHQgbm90IGNvbW11bmljYXRlIG51bWJlcnMgc3BlY2lmaWNhbGx5IChpLmUuLCBuZXR3b3JrIGdyYXBocykuIFdlIGNhbiB0aGVuIHVzZSB0aGUgZmlndXJlcyB0byBleHBsb3JlIHByb3BlcnRpZXMgb2YgdGhlIHRleHQgdGhhdCBtaWdodCBub3QgYmUgaW1tZWRpYXRlbHkgYXBwYXJlbnQgZnJvbSBjbG9zZSByZWFkaW5nLgoKQXMgYSBxdWljayAodmlzdWFsKSBzdW1tYXJ5IG9mIHdoYXQncyBiZWVuIGNvdmVyZWQsIGhlcmUgaXMgYSB0YWJsZSBvZiBwbG90IHR5cGVzIGFuZCB0aGVpciAoYmVzdCkgdXNlczoKClBsb3QgfCBVc2VzCi0tLXwtLS0tLS0tLS0tCkJhciBwbG90IHwgRnJlcXVlbmN5IChyZWxhdGl2ZSBvciBhYnNvbHV0ZSkKSGlzdG9ncmFtIHwgRnJlcXVlbmN5IGRpc3RyaWJ1dGlvbiBhY3Jvc3MgYSBzY2FsZQpIZWF0bWFwIHwgRnJlcXVlbmN5L2F0dGVzdGF0aW9uIGFjcm9zcyBtdWx0aXBsZSBkaW1lbnNpb25zCk5ldHdvcmsgZ3JhcGggfCBDb2xsb2NhdGlvbiAod2l0aCBvciB3aXRob3V0IGRpcmVjdGlvbmFsaXR5KQoKCk90aGVyIFBsb3RzIHwgVXNlcwotLS18LS0tLS0tLS0tLQpGbG93IGNoYXJ0IHwgVGVtcG9yYWwgb3JkZXIvYnJhbmNoaW5nIHdpdGhvdXQgcmVnYXJkIHRvIHNwYWNpYWwgb3JpZW50YXRpb24KQ2hvcmQgZGlhZ3JhbSB8IENvbm5lY3QgY29uY2VwdHMgYnkgZnJlcXVlbmN5IGJ1dCBjYW4gYmUgYnVzeSBvciBjb25mdXNpbmcKQ2hvcm9wbGV0aCBtYXAgfCBNYXAgb3IgZGlhZ3JhbSB0aGF0IHVzZXMgc2hhZGluZy9jb2xvciB0byBpbmRpY2F0ZSB2YWx1ZQpXb3JkIGNsb3VkIHwgKkNBVVRJT046KiBQcmV0dHkgYnV0IGRpZmZpY3VsdCB0byBjb21wYXJlIHJlbGF0aXZlIHNpemVzICAKUGllIGNoYXJ0IHwgKipETyBOT1QgVVNFKio6IERpZmZpY3VsdCB0byBjb21wYXJlIHJlbGF0aXZlIHNpemUgb2Ygc2xpY2VzCg==